Forecasting
Estimation window
train <- ihsg %>% filter(date >= forecast_start %m-% years(train_years))
nrow(train)
[1] 711
range(train$date)
[1] "2023-11-02" "2026-10-30"
Weekday effects in returns
Calendar effects are the only information about the forecast period
that is known in advance. If mean returns differ by weekday, weekday
dummies can make point forecasts vary from day to day; if they do not,
any such variation is sampling noise. Note that Monday returns span the
weekend.
ret_dow <- train %>%
mutate(r = 100 * log(close / lag(close)),
dow = factor(lubridate::wday(date, week_start = 1), levels = 1:5,
labels = c("Mon", "Tue", "Wed", "Thu", "Fri"))) %>%
filter(!is.na(r))
# Daily log returns (%) by weekday
ret_dow %>%
group_by(dow) %>%
summarise(n = n(), mean_return = mean(r), median_return = median(r), sd = sd(r)) %>%
mutate(across(where(is.double), ~ round(.x, 3)))
# Rank-based test for differences between weekdays (robust to fat tails)
kruskal.test(r ~ dow, data = ret_dow)
Kruskal-Wallis rank sum test
data: r by dow
Kruskal-Wallis chi-squared = 3.0788, df = 4, p-value = 0.5447
Candidate models
Daily index levels behave close to a random walk, so the candidate
set combines benchmarks for the level with a volatility model for the
uncertainty:
- Random walk and random walk with
drift: the benchmarks every model must beat.
- ARIMA and ETS on the log scale
(
lambda = 0), which keeps prediction intervals
positive.
- Theta: a strong benchmark in the M3 and M4
forecasting competitions.
- TBATS with weekly (5) and annual trading-day
cycles, the annual period estimated from the data.
- NNAR: a neural network autoregression.
- GJR-GARCH(1,1) with Student-t errors on daily log
returns r_t = \log(P_t / P_{t-1}):
r_t = \mu + \varepsilon_t, \qquad \varepsilon_t = \sigma_t z_t, \qquad
z_t \sim t_\nu
\sigma_t^2 = \omega + \left(\alpha + \gamma\,
\mathbb{1}\{\varepsilon_{t-1} < 0\}\right)\varepsilon_{t-1}^2 +
\beta\,\sigma_{t-1}^2
where \gamma captures the leverage
effect (negative shocks raise volatility more than positive shocks of
the same size). Price forecasts and intervals are obtained from 5,000
simulated return paths.
The ARIMA + weekday model regresses daily log
returns (in %) on weekday dummies, with Monday as the baseline and ARMA
errors:
r_t = \beta_0 + \sum_{d = \text{Tue}}^{\text{Fri}} \beta_d D_{d,t} +
u_t, \qquad u_t \sim \text{ARMA}(p, q)
Its price forecasts and intervals come from 2,000 bootstrapped
simulated paths.
library(rugarch)
garch_spec <- ugarchspec(
variance.model = list(model = "gjrGARCH", garchOrder = c(1, 1)),
mean.model = list(armaOrder = c(0, 0), include.mean = TRUE),
distribution.model = "std"
)
# GJR-GARCH price forecasts from simulated log-return paths
fc_garch <- function(y, h, nsim = 5000) {
fit <- ugarchfit(garch_spec, data = diff(log(y)), solver = "hybrid")
if (convergence(fit) != 0) stop("GARCH estimation did not converge")
sim <- ugarchsim(fit, n.sim = h, m.sim = nsim, startMethod = "sample")
ret <- matrix(fitted(sim), nrow = h)
paths <- matrix(tail(y, 1) * exp(apply(ret, 2, cumsum)), nrow = h)
q <- function(p) apply(paths, 1, quantile, probs = p)
list(method = "GJR-GARCH(1,1), Student-t",
mean = rowMeans(paths),
lower = cbind(q(0.100), q(0.025)),
upper = cbind(q(0.900), q(0.975)),
model = fit)
}
# Weekday dummies (Monday is the baseline)
dow_matrix <- function(dates) {
dow <- factor(lubridate::wday(dates, week_start = 1), levels = 1:5,
labels = c("Mon", "Tue", "Wed", "Thu", "Fri"))
model.matrix(~ dow)[, -1, drop = FALSE]
}
# Weekday regression with ARMA errors on log returns (%); needs the dates of the
# estimation window and of the trading days to be forecast
fc_weekday <- function(y, dates, new_dates, nsim = 2000) {
h <- length(new_dates)
r <- 100 * diff(log(y))
fit <- auto.arima(r, xreg = dow_matrix(dates[-1]), d = 0)
sims <- replicate(nsim, as.numeric(simulate(fit, nsim = h, xreg = dow_matrix(new_dates),
future = TRUE, bootstrap = TRUE)))
paths <- matrix(tail(y, 1) * exp(apply(matrix(sims, nrow = h) / 100, 2, cumsum)), nrow = h)
q <- function(p) apply(paths, 1, quantile, probs = p)
list(method = sprintf("Weekday + ARMA(%d,%d) errors", fit$arma[1], fit$arma[2]),
mean = rowMeans(paths),
lower = cbind(q(0.100), q(0.025)),
upper = cbind(q(0.900), q(0.975)),
model = fit)
}
# Each function takes a numeric price vector y and forecasts h trading days ahead
fc_funs <- list(
"Random walk" = function(y, h) naive(ts(y), h = h, level = lv),
"Random walk with drift" = function(y, h) rwf(ts(y), drift = TRUE, h = h, level = lv),
"ARIMA" = function(y, h) forecast(auto.arima(ts(y), lambda = 0, biasadj = TRUE),
h = h, level = lv),
"ETS" = function(y, h) forecast(ets(ts(y), lambda = 0, biasadj = TRUE),
h = h, level = lv),
"Theta" = function(y, h) thetaf(ts(y), h = h, level = lv),
"TBATS" = function(y, h) forecast(tbats(msts(y, seasonal.periods = c(5, annual_period)),
use.box.cox = TRUE, use.parallel = FALSE),
h = h, level = lv),
"NNAR" = function(y, h) forecast(nnetar(ts(y), lambda = 0),
h = h, PI = TRUE, level = lv),
"GJR-GARCH" = function(y, h) fc_garch(y, h)
)
# Convert a forecast object (or a simulation-based list) into a tidy tibble
tidy_fc <- function(fc, model, h) {
if (is.null(fc)) {
return(tibble(model = model, method = NA_character_, horizon = seq_len(h),
mean = NA_real_, lo80 = NA_real_, hi80 = NA_real_,
lo95 = NA_real_, hi95 = NA_real_))
}
tibble(model = model,
method = if (is.null(fc$method)) model else fc$method,
horizon = seq_len(h),
mean = as.numeric(fc$mean),
lo80 = as.numeric(fc$lower[, 1]), hi80 = as.numeric(fc$upper[, 1]),
lo95 = as.numeric(fc$lower[, 2]), hi95 = as.numeric(fc$upper[, 2]))
}
# Fit the selected price-only models on price vector y; failed fits return NA rows
run_models <- function(y, h, models = names(fc_funs)) {
fcs <- map(fc_funs[models], ~ tryCatch(.x(y, h), error = function(e) NULL))
imap_dfr(fcs, ~ tidy_fc(.x, .y, h))
}
Forecasts for 2 to 10 November 2026
set.seed(123)
fc_objects <- map(fc_funs, ~ tryCatch(.x(train$close, h), error = function(e) NULL))
# The weekday model also needs dates; list() keeps a failed fit as NULL
fc_objects["ARIMA + weekday"] <- list(
tryCatch(fc_weekday(train$close, train$date, future_dates), error = function(e) NULL)
)
fc_final <- imap_dfr(fc_objects, ~ tidy_fc(.x, .y, h)) %>%
mutate(date = future_dates[horizon])
# Specification selected for each model
fc_final %>% distinct(model, method)
garch_fit <- fc_objects[["GJR-GARCH"]]$model
wd_fit <- fc_objects[["ARIMA + weekday"]]$model
# GJR-GARCH estimates, persistence, and half-life of volatility shocks (trading days)
if (!is.null(garch_fit)) {
print(round(garch_fit@fit$matcoef, 4))
print(c(persistence = persistence(garch_fit), half_life_days = halflife(garch_fit)))
}
Estimate Std. Error t value Pr(>|t|)
mu 0.0005 0.0003 1.6035 0.1088
omega 0.0000 0.0000 38.2196 0.0000
alpha1 0.0000 0.0140 0.0000 1.0000
beta1 0.7942 0.0267 29.7512 0.0000
gamma1 0.2553 0.0585 4.3632 0.0000
shape 5.3398 0.8927 5.9814 0.0000
persistence half_life_days
0.9218104 8.5136758
# Weekday regression with ARMA errors (returns in %)
if (!is.null(wd_fit)) print(wd_fit)
Series: r
Regression with ARIMA(3,0,0) errors
Coefficients:
ar1 ar2 ar3 dowTue dowWed dowThu dowFri
0.0669 -0.0799 0.0829 0.0722 0.0650 -0.0363 -0.2137
s.e. 0.0374 0.0377 0.0376 0.1127 0.1132 0.1132 0.1146
sigma^2 = 1.847: log likelihood = -1221.75
AIC=2459.51 AICc=2459.71 BIC=2496.03
# Point forecasts by model
fc_final %>%
select(date, model, mean) %>%
pivot_wider(names_from = model, values_from = mean) %>%
mutate(across(-date, ~ round(.x, 1)))
# Point forecasts with 80% and 95% prediction intervals
fc_final %>%
select(model, date, mean, lo80, hi80, lo95, hi95) %>%
mutate(across(mean:hi95, ~ round(.x, 1)))
plot_fc <- function(fc, hist, ncol = 2) {
fc <- fc %>% mutate(model = factor(model, levels = unique(model)))
last_obs <- slice_tail(hist, n = 1)
# Connect the last observation to each forecast line
bridge <- distinct(fc, model) %>%
mutate(date = last_obs$date, mean = last_obs$close,
lo80 = mean, hi80 = mean, lo95 = mean, hi95 = mean)
fc_line <- bind_rows(fc, bridge) %>% arrange(model, date)
hist_rep <- cross_join(distinct(fc, model), select(hist, date, close))
ggplot() +
geom_ribbon(data = fc_line, aes(date, ymin = lo95, ymax = hi95, fill = "95% interval"),
alpha = 0.35) +
geom_ribbon(data = fc_line, aes(date, ymin = lo80, ymax = hi80, fill = "80% interval"),
alpha = 0.55) +
geom_line(data = hist_rep, aes(date, close, colour = "Actual"), linewidth = 0.5) +
geom_line(data = fc_line, aes(date, mean, colour = "Point forecast"), linewidth = 0.7) +
geom_point(data = fc, aes(date, mean, colour = "Point forecast"), size = 1.2) +
facet_wrap(~ model, ncol = ncol) +
scale_fill_manual(values = c("95% interval" = "#c6d9ec", "80% interval" = "#8fb3d9"),
breaks = c("80% interval", "95% interval")) +
scale_colour_manual(values = c("Actual" = "grey25", "Point forecast" = pal[["orange"]])) +
scale_x_date(date_breaks = "2 weeks", date_labels = "%d %b") +
scale_y_continuous(labels = comma) +
labs(x = NULL, y = "Index level") +
theme_ihsg()
}
# Use the selected specification as the panel title where it is informative
fc_plot_data <- fc_final %>%
mutate(model = if_else(model %in% c("ARIMA", "ETS", "TBATS", "NNAR",
"GJR-GARCH", "ARIMA + weekday"),
coalesce(method, model), model))
p <- plot_fc(fc_plot_data, slice_tail(train, n = 40), ncol = 3) +
labs(title = "JKSE Closing Price Forecasts, 2 to 10 November 2026",
subtitle = "Last 40 trading days with point forecasts and 80% / 95% prediction intervals",
caption = source_label)
hd_plot(p, "03_forecasts_by_model", width = 10, height = 9)

Simulated price paths
The figure shows 30 of 2,000 paths simulated from the fitted
GJR-GARCH model. Individual paths move up and down, but because the
direction of each move is unpredictable, their average is nearly flat.
The bands reflect the current level of volatility.
if (is.null(garch_fit)) stop("The GJR-GARCH fit is not available")
set.seed(123)
n_paths <- 2000
last_close <- tail(train$close, 1)
sim <- ugarchsim(garch_fit, n.sim = h, m.sim = n_paths, startMethod = "sample")
ret <- matrix(fitted(sim), nrow = h)
paths <- rbind(rep(last_close, n_paths),
last_close * exp(apply(ret, 2, cumsum)))
colnames(paths) <- paste0("path_", seq_len(n_paths))
path_tbl <- as_tibble(paths) %>%
mutate(date = c(max(train$date), future_dates)) %>%
pivot_longer(-date, names_to = "path", values_to = "close")
band_tbl <- path_tbl %>%
group_by(date) %>%
summarise(mean = mean(close),
lo80 = quantile(close, 0.100), hi80 = quantile(close, 0.900),
lo95 = quantile(close, 0.025), hi95 = quantile(close, 0.975),
.groups = "drop")
shown <- sample(colnames(paths), 30)
p <- ggplot() +
geom_ribbon(data = band_tbl, aes(date, ymin = lo95, ymax = hi95),
fill = "#c6d9ec", alpha = 0.7) +
geom_ribbon(data = band_tbl, aes(date, ymin = lo80, ymax = hi80),
fill = "#8fb3d9", alpha = 0.7) +
geom_line(data = filter(path_tbl, path %in% shown),
aes(date, close, group = path),
colour = pal[["navy"]], alpha = 0.4, linewidth = 0.35) +
geom_line(data = slice_tail(train, n = 40), aes(date, close),
colour = "grey25", linewidth = 0.6) +
geom_line(data = band_tbl, aes(date, mean), colour = pal[["orange"]], linewidth = 1) +
scale_x_date(date_breaks = "2 weeks", date_labels = "%d %b") +
scale_y_continuous(labels = comma) +
labs(title = "Simulated JKSE Price Paths, 2 to 10 November 2026",
subtitle = "30 of 2,000 GJR-GARCH paths (blue), their mean (orange), and 80% / 95% bands",
x = NULL, y = "Index level", caption = source_label) +
theme_ihsg()
hd_plot(p, "04_simulated_paths")

Rolling-origin evaluation
A single holdout of a few days is too noisy to rank models. Each
model is re-estimated on a rolling window of the same length as the
final estimation window, at forecast origins spaced 5 trading days apart
over the last 250 trading days. The ARIMA + weekday model uses the dates
of each window and the actual trading dates that follow each origin,
which are known in advance from the IDX calendar. Errors are averaged
over all origins and horizons. Coverage close to 0.80 and 0.95 indicates
well-calibrated prediction intervals. TBATS and NNAR are only included
when run_slow_backtest = TRUE.
bt_models <- c("Random walk", "Random walk with drift", "ARIMA", "ETS", "Theta",
"GJR-GARCH", "ARIMA + weekday")
if (run_slow_backtest) bt_models <- c(bt_models, "TBATS", "NNAR")
y_all <- ihsg$close
n_win <- nrow(train) # same window length as the final models
origins <- seq(length(y_all) - 250, length(y_all) - h, by = 5)
set.seed(123)
bt <- map_dfr(origins, function(o) {
idx <- (o - n_win + 1):o
# Models that use prices only
fc_price <- run_models(y_all[idx], h, models = intersect(bt_models, names(fc_funs)))
# Weekday model: also needs the window dates and the next h trading dates
wd_obj <- tryCatch(fc_weekday(y_all[idx], ihsg$date[idx], ihsg$date[o + seq_len(h)],
nsim = 1000),
error = function(e) NULL)
bind_rows(fc_price, tidy_fc(wd_obj, "ARIMA + weekday", h)) %>%
mutate(origin = o,
origin_date = ihsg$date[o],
actual = y_all[o + horizon])
})
saveRDS(bt, "backtest_results.rds")
bt %>%
group_by(model) %>%
summarise(
RMSE = sqrt(mean((actual - mean)^2, na.rm = TRUE)),
MAE = mean(abs(actual - mean), na.rm = TRUE),
MAPE = 100 * mean(abs((actual - mean) / actual), na.rm = TRUE),
`Coverage 80%` = mean(actual >= lo80 & actual <= hi80, na.rm = TRUE),
`Coverage 95%` = mean(actual >= lo95 & actual <= hi95, na.rm = TRUE),
`Failed fits` = sum(is.na(mean)) / h,
.groups = "drop"
) %>%
mutate(`RMSE vs RW` = RMSE / RMSE[model == "Random walk"]) %>%
relocate(`RMSE vs RW`, .after = RMSE) %>%
arrange(RMSE) %>%
mutate(across(where(is.numeric), ~ round(.x, 3)))
rmse_h <- bt %>%
group_by(model, horizon) %>%
summarise(RMSE = sqrt(mean((actual - mean)^2, na.rm = TRUE)), .groups = "drop") %>%
group_by(horizon) %>%
mutate(rel_rmse = RMSE / RMSE[model == "Random walk"]) %>%
ungroup() %>%
mutate(model = factor(model, levels = bt_models))
# Nine distinct colours, enough for all models when run_slow_backtest = TRUE
model_cols <- c("#1f4e79", "#e07b39", "#2e8b57", "#c0392b", "#7b3294",
"#8c6d31", "#17becf", "#e377c2", "grey40")
p <- ggplot(rmse_h, aes(horizon, rel_rmse, colour = model)) +
geom_hline(yintercept = 1, linetype = "dashed", colour = "grey40") +
geom_line(linewidth = 0.8) +
geom_point(size = 1.8) +
scale_colour_manual(values = model_cols) +
scale_x_continuous(breaks = seq_len(h)) +
guides(colour = guide_legend(nrow = 2)) +
labs(title = "Forecast Accuracy Relative to the Random Walk",
subtitle = "RMSE ratio by horizon across all origins; values below 1 beat the random walk",
x = "Forecast horizon (trading days)", y = "Relative RMSE",
caption = source_label) +
theme_ihsg()
hd_plot(p, "05_relative_rmse", height = 4.5)

Diebold–Mariano tests compare one-step-ahead squared errors of each
model with the random walk. A negative statistic means the model has
smaller errors; a p-value below 0.05 indicates that the difference is
statistically significant.
e1 <- bt %>%
filter(horizon == 1) %>%
transmute(origin, model, error = actual - mean) %>%
pivot_wider(names_from = model, values_from = error)
map_dfr(setdiff(bt_models, "Random walk"), function(m) {
ok <- complete.cases(e1[[m]], e1[["Random walk"]])
test <- dm.test(e1[[m]][ok], e1[["Random walk"]][ok], h = 1, power = 2)
tibble(model = m, DM_statistic = unname(test$statistic), p_value = test$p.value)
}) %>%
mutate(across(where(is.numeric), ~ round(.x, 3)))
LS0tCnRpdGxlOiAiSktTRSBJbmRleCBBbmFseXNpcyIKb3V0cHV0OgogIGh0bWxfbm90ZWJvb2s6CiAgICBtYXRoX21ldGhvZDoga2F0ZXgKICAgIHRoZW1lOiB5ZXRpCiAgICB0b2M6IHRydWUKICAgIHRvY19mbG9hdDoKICAgICAgdG9jX2NvbGxhcHNlZDogdHJ1ZQogICAgbnVtYmVyX3NlY3Rpb25zOiBmYWxzZQogICAgZGZfcHJpbnQ6IHBhZ2VkCi0tLQoKIyMgTGlicmFyaWVzCgpgYGB7ciBzZXR1cH0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KAoJZmlnLmFsaWduID0gImNlbnRlciIsCgltZXNzYWdlID0gRkFMU0UsCgl3YXJuaW5nID0gRkFMU0UsCglvdXQud2lkdGggPSAiMTAwJSIKKQpsaWJyYXJ5KHRpZHl2ZXJzZSkKbGlicmFyeSh0aWR5cXVhbnQpCmxpYnJhcnkoZm9yZWNhc3QpCmxpYnJhcnkoZ2dyZXBlbCkKbGlicmFyeShzY2FsZXMpCgojIFNhdmUgYSBnZ3Bsb3QgYXMgYSBoaWdoLXJlc29sdXRpb24gUE5HIGFuZCBkaXNwbGF5IGl0IGlubGluZQpoZF9wbG90IDwtIGZ1bmN0aW9uKHAsIG5hbWUsIHdpZHRoID0gOSwgaGVpZ2h0ID0gNSwgZHBpID0gMzAwKSB7CiAgZGlyLmNyZWF0ZSgiZmlndXJlcyIsIHNob3dXYXJuaW5ncyA9IEZBTFNFKQogIHBhdGggPC0gZmlsZS5wYXRoKCJmaWd1cmVzIiwgcGFzdGUwKG5hbWUsICIucG5nIikpCiAgZ2dzYXZlKHBhdGgsIHBsb3QgPSBwLCB3aWR0aCA9IHdpZHRoLCBoZWlnaHQgPSBoZWlnaHQsIGRwaSA9IGRwaSwKICAgICAgICAgZGV2aWNlID0gcmFnZzo6YWdnX3BuZywgYmcgPSAid2hpdGUiKQogIGtuaXRyOjppbmNsdWRlX2dyYXBoaWNzKHBhdGgpCn0KCiMgQ29sb3VyIHBhbGV0dGUgYW5kIHRoZW1lCnBhbCA8LSBjKG5hdnkgPSAiIzFmNGU3OSIsIG9yYW5nZSA9ICIjZTA3YjM5IiwgZ3JlZW4gPSAiIzJlOGI1NyIsCiAgICAgICAgIHJlZCA9ICIjYzAzOTJiIiwgZ3JleSA9ICJncmV5NTUiKQoKdGhlbWVfaWhzZyA8LSBmdW5jdGlvbihiYXNlX3NpemUgPSAxMikgewogIHRoZW1lX21pbmltYWwoYmFzZV9zaXplID0gYmFzZV9zaXplKSArCiAgICB0aGVtZSgKICAgICAgcGxvdC50aXRsZSAgICAgICAgICAgPSBlbGVtZW50X3RleHQoZmFjZSA9ICJib2xkIiwgc2l6ZSA9IHJlbCgxLjI1KSksCiAgICAgIHBsb3Quc3VidGl0bGUgICAgICAgID0gZWxlbWVudF90ZXh0KGNvbG91ciA9ICJncmV5NDAiLCBtYXJnaW4gPSBtYXJnaW4oYiA9IDgpKSwKICAgICAgcGxvdC5jYXB0aW9uICAgICAgICAgPSBlbGVtZW50X3RleHQoY29sb3VyID0gImdyZXk1MCIsIHNpemUgPSByZWwoMC43NSkpLAogICAgICBwbG90LnRpdGxlLnBvc2l0aW9uICA9ICJwbG90IiwKICAgICAgcGFuZWwuZ3JpZC5taW5vciAgICAgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgIGxlZ2VuZC5wb3NpdGlvbiAgICAgID0gInRvcCIsCiAgICAgIGxlZ2VuZC5qdXN0aWZpY2F0aW9uID0gImxlZnQiLAogICAgICBsZWdlbmQudGl0bGUgICAgICAgICA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgc3RyaXAudGV4dCAgICAgICAgICAgPSBlbGVtZW50X3RleHQoZmFjZSA9ICJib2xkIiwgaGp1c3QgPSAwKQogICAgKQp9Cgpzb3VyY2VfbGFiZWwgPC0gIlNvdXJjZTogWWFob28gRmluYW5jZSAoXkpLU0UpIgpgYGAKCiMjIFNldHRpbmdzCgpgYGB7cn0KZm9yZWNhc3Rfc3RhcnQgICAgPC0gYXMuRGF0ZSgiMjAyNi0xMS0wMiIpCmZvcmVjYXN0X2VuZCAgICAgIDwtIGFzLkRhdGUoIjIwMjYtMTEtMTAiKQp0cmFpbl95ZWFycyAgICAgICA8LSAzICAgICAgICMgbGVuZ3RoIG9mIHRoZSBlc3RpbWF0aW9uIHdpbmRvdyAoeWVhcnMpCnJ1bl9zbG93X2JhY2t0ZXN0IDwtIEZBTFNFICAgIyBUUlVFIGFkZHMgVEJBVFMgYW5kIE5OQVIgdG8gdGhlIGJhY2t0ZXN0IChtdWNoIHNsb3dlcikKbHYgICAgICAgICAgICAgICAgPC0gYyg4MCwgOTUpCgojIElEWCBob2xpZGF5cyB0aGF0IGNvdWxkIGZhbGwgaW5zaWRlIGEgZm9yZWNhc3QgaG9yaXpvbiAoMjAyNiBjYWxlbmRhcik7IGV4dGVuZCBhcyBuZWVkZWQKaWR4X2hvbGlkYXlzIDwtIGFzLkRhdGUoYygiMjAyNi0xMi0yNCIsICIyMDI2LTEyLTI1IiwgIjIwMjYtMTItMzEiKSkKCiMgVHJhZGluZyBkYXlzIGluIHRoZSBmb3JlY2FzdCBob3Jpem9uCmZ1dHVyZV9kYXRlcyA8LSBzZXEoZm9yZWNhc3Rfc3RhcnQsIGZvcmVjYXN0X2VuZCwgYnkgPSAiZGF5IikKZnV0dXJlX2RhdGVzIDwtIGZ1dHVyZV9kYXRlc1tsdWJyaWRhdGU6OndkYXkoZnV0dXJlX2RhdGVzLCB3ZWVrX3N0YXJ0ID0gMSkgPD0gNSAmCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAhZnV0dXJlX2RhdGVzICVpbiUgaWR4X2hvbGlkYXlzXQpoIDwtIGxlbmd0aChmdXR1cmVfZGF0ZXMpCgpmdXR1cmVfZGF0ZXMKYGBgCgojIyBEYXRhCgpEYWlseSBkYXRhIGFyZSBkb3dubG9hZGVkIGZyb20gWWFob28gRmluYW5jZS4gV2Vla2VuZCBkYXRlcyBhbmQgZmxhdCBiYXJzCihvcGVuID0gaGlnaCA9IGxvdyA9IGNsb3NlKSwgd2hpY2ggWWFob28gb2NjYXNpb25hbGx5IHJlY29yZHMgb24gSURYIGhvbGlkYXlzLAphcmUgbGlzdGVkIGZpcnN0IGFuZCB0aGVuIHJlbW92ZWQuCgpgYGB7cn0KaWhzZ19yYXcgPC0gdHFfZ2V0KCJeSktTRSIsIGZyb20gPSAiMjAxNS0wMS0wMSIpCgppaHNnX2ZsYWdnZWQgPC0gaWhzZ19yYXcgJT4lCiAgZHBseXI6OmZpbHRlcighaXMubmEoY2xvc2UpLCBkYXRlIDwgZm9yZWNhc3Rfc3RhcnQpICU+JQogIGRpc3RpbmN0KGRhdGUsIC5rZWVwX2FsbCA9IFRSVUUpICU+JQogIGFycmFuZ2UoZGF0ZSkgJT4lCiAgbXV0YXRlKAogICAgd2Vla2VuZCAgPSBsdWJyaWRhdGU6OndkYXkoZGF0ZSwgd2Vla19zdGFydCA9IDEpID4gNSwKICAgIGZsYXRfYmFyID0gY29hbGVzY2Uob3BlbiA9PSBoaWdoICYgaGlnaCA9PSBsb3cgJiBsb3cgPT0gY2xvc2UsIEZBTFNFKQogICkKCiMgUm93cyB0byBiZSByZW1vdmVkCmloc2dfZmxhZ2dlZCAlPiUgZmlsdGVyKHdlZWtlbmQgfCBmbGF0X2JhcikKCmloc2cgPC0gaWhzZ19mbGFnZ2VkICU+JQogIGZpbHRlcighd2Vla2VuZCwgIWZsYXRfYmFyKSAlPiUKICBzZWxlY3QoZGF0ZSwgY2xvc2UpCgp0YWlsKGloc2cpCmBgYAoKIyMgVHJhZGluZyBDYWxlbmRhcgoKVGhlIHNlcmllcyBjb250YWlucyBJRFggdHJhZGluZyBkYXlzIG9ubHksIHNvIGdhcHMgbG9uZ2VyIHRoYW4gYSB3ZWVrZW5kCmNvcnJlc3BvbmQgdG8gbmF0aW9uYWwgaG9saWRheXMgYW5kIGNvbGxlY3RpdmUgbGVhdmUgZGF5cy4KCmBgYHtyfQojIExvbmdlc3QgZ2FwcyBiZXR3ZWVuIGNvbnNlY3V0aXZlIHRyYWRpbmcgZGF0ZXMKaWhzZyAlPiUKICBtdXRhdGUocHJldmlvdXNfZGF0ZSA9IGxhZyhkYXRlKSwKICAgICAgICAgZ2FwX2RheXMgICAgICA9IGFzLm51bWVyaWMoZGF0ZSAtIHByZXZpb3VzX2RhdGUpKSAlPiUKICBmaWx0ZXIoZ2FwX2RheXMgPiAzKSAlPiUKICBhcnJhbmdlKGRlc2MoZ2FwX2RheXMpKSAlPiUKICBzZWxlY3QocHJldmlvdXNfZGF0ZSwgZGF0ZSwgZ2FwX2RheXMpICU+JQogIGhlYWQoMTApCmBgYAoKYGBge3J9CiMgVHJhZGluZyBkYXlzIHBlciBjb21wbGV0ZSBjYWxlbmRhciB5ZWFyCnRyYWRpbmdfZGF5cyA8LSBpaHNnICU+JQogIGNvdW50KHllYXIgPSBsdWJyaWRhdGU6OnllYXIoZGF0ZSksIG5hbWUgPSAidHJhZGluZ19kYXlzIikgJT4lCiAgZmlsdGVyKHllYXIgPCBsdWJyaWRhdGU6OnllYXIobWF4KGloc2ckZGF0ZSkpKQoKdHJhZGluZ19kYXlzCgojIEFubnVhbCBzZWFzb25hbCBwZXJpb2QgZm9yIFRCQVRTOiBtZWFuIG9mIHRoZSBsYXN0IGZpdmUgY29tcGxldGUgeWVhcnMKYW5udWFsX3BlcmlvZCA8LSByb3VuZChtZWFuKHRhaWwodHJhZGluZ19kYXlzJHRyYWRpbmdfZGF5cywgNSkpKQphbm51YWxfcGVyaW9kCmBgYAoKIyMgQ2xvc2luZyBQcmljZQoKIyMjIEZ1bGwgaGlzdG9yeQoKYGBge3J9CmtleV9wb2ludHMgPC0gYmluZF9yb3dzKAogIGloc2cgJT4lIHNsaWNlX21heChjbG9zZSwgbiA9IDEsIHdpdGhfdGllcyA9IEZBTFNFKSAlPiUgbXV0YXRlKHRhZyA9ICJBbGwtdGltZSBoaWdoIiksCiAgaWhzZyAlPiUgc2xpY2VfbWluKGNsb3NlLCBuID0gMSwgd2l0aF90aWVzID0gRkFMU0UpICU+JSBtdXRhdGUodGFnID0gIkxvd2VzdCBjbG9zZSIpLAogIGloc2cgJT4lIHNsaWNlX3RhaWwobiA9IDEpICAgICAgICAgICAgICAgICAgICAgICAgICAlPiUgbXV0YXRlKHRhZyA9ICJMYXRlc3QiKQopICU+JQogIG11dGF0ZShsYWJlbCA9IHBhc3RlMCh0YWcsICJcbiIsIGZvcm1hdChkYXRlLCAiJWQgJWIgJVkiKSwgIlxuIiwKICAgICAgICAgICAgICAgICAgICAgICAgY29tbWEoY2xvc2UsIGFjY3VyYWN5ID0gMSkpKQoKcCA8LSBnZ3Bsb3QoaWhzZywgYWVzKGRhdGUsIGNsb3NlKSkgKwogIGdlb21fbGluZShhZXMoY29sb3VyID0gIkRhaWx5IGNsb3NlIiksIGxpbmV3aWR0aCA9IDAuMzUpICsKICBnZW9tX21hKGFlcyhjb2xvdXIgPSAiNTAtZGF5IE1BIiksICBtYV9mdW4gPSBTTUEsIG4gPSA1MCwKICAgICAgICAgIGxpbmV0eXBlID0gInNvbGlkIiwgbGluZXdpZHRoID0gMC44KSArCiAgZ2VvbV9tYShhZXMoY29sb3VyID0gIjIwMC1kYXkgTUEiKSwgbWFfZnVuID0gU01BLCBuID0gMjAwLAogICAgICAgICAgbGluZXR5cGUgPSAic29saWQiLCBsaW5ld2lkdGggPSAwLjgpICsKICBnZW9tX3BvaW50KGRhdGEgPSBrZXlfcG9pbnRzLCBzaXplID0gMi4yKSArCiAgZ2VvbV9sYWJlbF9yZXBlbChkYXRhID0ga2V5X3BvaW50cywgYWVzKGxhYmVsID0gbGFiZWwpLAogICAgICAgICAgICAgICAgICAgc2l6ZSA9IDMsIGxhYmVsLnNpemUgPSAwLjE1LCBmaWxsID0gYWxwaGEoIndoaXRlIiwgMC45KSwKICAgICAgICAgICAgICAgICAgIGJveC5wYWRkaW5nID0gMC42LCBtaW4uc2VnbWVudC5sZW5ndGggPSAwLCBzZWVkID0gNDIpICsKICBzY2FsZV9jb2xvdXJfbWFudWFsKHZhbHVlcyA9IGMoIkRhaWx5IGNsb3NlIiA9IHBhbFtbImdyZXkiXV0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICI1MC1kYXkgTUEiICAgPSBwYWxbWyJvcmFuZ2UiXV0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICIyMDAtZGF5IE1BIiAgPSBwYWxbWyJuYXZ5Il1dKSwKICAgICAgICAgICAgICAgICAgICAgIGJyZWFrcyA9IGMoIkRhaWx5IGNsb3NlIiwgIjUwLWRheSBNQSIsICIyMDAtZGF5IE1BIikpICsKICBzY2FsZV94X2RhdGUoZGF0ZV9icmVha3MgPSAiMSB5ZWFyIiwgZGF0ZV9sYWJlbHMgPSAiJVkiLAogICAgICAgICAgICAgICBleHBhbmQgPSBleHBhbnNpb24obXVsdCA9IGMoMC4wMSwgMC4wMykpKSArCiAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscyA9IGNvbW1hLCBleHBhbmQgPSBleHBhbnNpb24obXVsdCA9IDAuMDgpKSArCiAgbGFicyh0aXRsZSA9ICJKYWthcnRhIENvbXBvc2l0ZSBJbmRleCAoSktTRSkiLAogICAgICAgc3VidGl0bGUgPSBwYXN0ZSgiRGFpbHkgY2xvc2luZyBwcmljZSwiLAogICAgICAgICAgICAgICAgICAgICAgICBmb3JtYXQobWluKGloc2ckZGF0ZSksICIlYiAlWSIpLCAidG8iLAogICAgICAgICAgICAgICAgICAgICAgICBmb3JtYXQobWF4KGloc2ckZGF0ZSksICIlYiAlWSIpKSwKICAgICAgIHggPSBOVUxMLCB5ID0gIkluZGV4IGxldmVsIiwgY2FwdGlvbiA9IHNvdXJjZV9sYWJlbCkgKwogIHRoZW1lX2loc2coKQoKaGRfcGxvdChwLCAiMDFfY2xvc2VfZnVsbCIpCmBgYAoKIyMjIExhc3QgMTIgbW9udGhzCgpgYGB7cn0KaWhzZ18xMm0gPC0gaWhzZyAlPiUgZmlsdGVyKGRhdGUgPj0gbWF4KGRhdGUpICVtLSUgbW9udGhzKDEyKSkKCnAgPC0gZ2dwbG90KGloc2dfMTJtLCBhZXMoZGF0ZSwgY2xvc2UpKSArCiAgZ2VvbV9yaWJib24oYWVzKHltaW4gPSBtaW4oY2xvc2UpLCB5bWF4ID0gY2xvc2UpLAogICAgICAgICAgICAgIGZpbGwgPSBwYWxbWyJuYXZ5Il1dLCBhbHBoYSA9IDAuMTIpICsKICBnZW9tX2xpbmUoY29sb3VyID0gcGFsW1sibmF2eSJdXSwgbGluZXdpZHRoID0gMC42KSArCiAgZ2VvbV9tYShtYV9mdW4gPSBTTUEsIG4gPSAyMCwgY29sb3VyID0gcGFsW1sib3JhbmdlIl1dLAogICAgICAgICAgbGluZXR5cGUgPSAic29saWQiLCBsaW5ld2lkdGggPSAwLjcpICsKICBzY2FsZV94X2RhdGUoZGF0ZV9icmVha3MgPSAiMSBtb250aCIsIGRhdGVfbGFiZWxzID0gIiViXG4lWSIpICsKICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gY29tbWEsIGV4cGFuZCA9IGV4cGFuc2lvbihtdWx0ID0gYygwLCAwLjA1KSkpICsKICBsYWJzKHRpdGxlID0gIkpLU0UgQ2xvc2luZyBQcmljZSwgTGFzdCAxMiBNb250aHMiLAogICAgICAgc3VidGl0bGUgPSAiT3JhbmdlIGxpbmU6IDIwLWRheSBzaW1wbGUgbW92aW5nIGF2ZXJhZ2UiLAogICAgICAgeCA9IE5VTEwsIHkgPSAiSW5kZXggbGV2ZWwiLCBjYXB0aW9uID0gc291cmNlX2xhYmVsKSArCiAgdGhlbWVfaWhzZygpCgpoZF9wbG90KHAsICIwMl9jbG9zZV8xMm0iKQpgYGAKCiMjIEZvcmVjYXN0aW5nCgojIyMgRXN0aW1hdGlvbiB3aW5kb3cKCmBgYHtyfQp0cmFpbiA8LSBpaHNnICU+JSBmaWx0ZXIoZGF0ZSA+PSBmb3JlY2FzdF9zdGFydCAlbS0lIHllYXJzKHRyYWluX3llYXJzKSkKCm5yb3codHJhaW4pCnJhbmdlKHRyYWluJGRhdGUpCmBgYAoKIyMjIFdlZWtkYXkgZWZmZWN0cyBpbiByZXR1cm5zCgpDYWxlbmRhciBlZmZlY3RzIGFyZSB0aGUgb25seSBpbmZvcm1hdGlvbiBhYm91dCB0aGUgZm9yZWNhc3QgcGVyaW9kIHRoYXQgaXMKa25vd24gaW4gYWR2YW5jZS4gSWYgbWVhbiByZXR1cm5zIGRpZmZlciBieSB3ZWVrZGF5LCB3ZWVrZGF5IGR1bW1pZXMgY2FuIG1ha2UKcG9pbnQgZm9yZWNhc3RzIHZhcnkgZnJvbSBkYXkgdG8gZGF5OyBpZiB0aGV5IGRvIG5vdCwgYW55IHN1Y2ggdmFyaWF0aW9uIGlzCnNhbXBsaW5nIG5vaXNlLiBOb3RlIHRoYXQgTW9uZGF5IHJldHVybnMgc3BhbiB0aGUgd2Vla2VuZC4KCmBgYHtyfQpyZXRfZG93IDwtIHRyYWluICU+JQogIG11dGF0ZShyICAgPSAxMDAgKiBsb2coY2xvc2UgLyBsYWcoY2xvc2UpKSwKICAgICAgICAgZG93ID0gZmFjdG9yKGx1YnJpZGF0ZTo6d2RheShkYXRlLCB3ZWVrX3N0YXJ0ID0gMSksIGxldmVscyA9IDE6NSwKICAgICAgICAgICAgICAgICAgICAgIGxhYmVscyA9IGMoIk1vbiIsICJUdWUiLCAiV2VkIiwgIlRodSIsICJGcmkiKSkpICU+JQogIGZpbHRlcighaXMubmEocikpCgojIERhaWx5IGxvZyByZXR1cm5zICglKSBieSB3ZWVrZGF5CnJldF9kb3cgJT4lCiAgZ3JvdXBfYnkoZG93KSAlPiUKICBzdW1tYXJpc2UobiA9IG4oKSwgbWVhbl9yZXR1cm4gPSBtZWFuKHIpLCBtZWRpYW5fcmV0dXJuID0gbWVkaWFuKHIpLCBzZCA9IHNkKHIpKSAlPiUKICBtdXRhdGUoYWNyb3NzKHdoZXJlKGlzLmRvdWJsZSksIH4gcm91bmQoLngsIDMpKSkKCiMgUmFuay1iYXNlZCB0ZXN0IGZvciBkaWZmZXJlbmNlcyBiZXR3ZWVuIHdlZWtkYXlzIChyb2J1c3QgdG8gZmF0IHRhaWxzKQprcnVza2FsLnRlc3QociB+IGRvdywgZGF0YSA9IHJldF9kb3cpCmBgYAoKIyMjIENhbmRpZGF0ZSBtb2RlbHMKCkRhaWx5IGluZGV4IGxldmVscyBiZWhhdmUgY2xvc2UgdG8gYSByYW5kb20gd2Fsaywgc28gdGhlIGNhbmRpZGF0ZSBzZXQgY29tYmluZXMKYmVuY2htYXJrcyBmb3IgdGhlIGxldmVsIHdpdGggYSB2b2xhdGlsaXR5IG1vZGVsIGZvciB0aGUgdW5jZXJ0YWludHk6CgotICoqUmFuZG9tIHdhbGsqKiBhbmQgKipyYW5kb20gd2FsayB3aXRoIGRyaWZ0Kio6IHRoZSBiZW5jaG1hcmtzIGV2ZXJ5IG1vZGVsIG11c3QgYmVhdC4KLSAqKkFSSU1BKiogYW5kICoqRVRTKiogb24gdGhlIGxvZyBzY2FsZSAoYGxhbWJkYSA9IDBgKSwgd2hpY2gga2VlcHMgcHJlZGljdGlvbiBpbnRlcnZhbHMgcG9zaXRpdmUuCi0gKipUaGV0YSoqOiBhIHN0cm9uZyBiZW5jaG1hcmsgaW4gdGhlIE0zIGFuZCBNNCBmb3JlY2FzdGluZyBjb21wZXRpdGlvbnMuCi0gKipUQkFUUyoqIHdpdGggd2Vla2x5ICg1KSBhbmQgYW5udWFsIHRyYWRpbmctZGF5IGN5Y2xlcywgdGhlIGFubnVhbCBwZXJpb2QgZXN0aW1hdGVkIGZyb20gdGhlIGRhdGEuCi0gKipOTkFSKio6IGEgbmV1cmFsIG5ldHdvcmsgYXV0b3JlZ3Jlc3Npb24uCi0gKipHSlItR0FSQ0goMSwxKSoqIHdpdGggU3R1ZGVudC10IGVycm9ycyBvbiBkYWlseSBsb2cgcmV0dXJucyAkcl90ID0gXGxvZyhQX3QgLyBQX3t0LTF9KSQ6CgokJApyX3QgPSBcbXUgKyBcdmFyZXBzaWxvbl90LCBccXF1YWQgXHZhcmVwc2lsb25fdCA9IFxzaWdtYV90IHpfdCwgXHFxdWFkIHpfdCBcc2ltIHRfXG51CiQkCgokJApcc2lnbWFfdF4yID0gXG9tZWdhICsgXGxlZnQoXGFscGhhICsgXGdhbW1hXCwgXG1hdGhiYnsxfVx7XHZhcmVwc2lsb25fe3QtMX0gPCAwXH1ccmlnaHQpXHZhcmVwc2lsb25fe3QtMX1eMiArIFxiZXRhXCxcc2lnbWFfe3QtMX1eMgokJAoKd2hlcmUgJFxnYW1tYSQgY2FwdHVyZXMgdGhlIGxldmVyYWdlIGVmZmVjdCAobmVnYXRpdmUgc2hvY2tzIHJhaXNlIHZvbGF0aWxpdHkgbW9yZQp0aGFuIHBvc2l0aXZlIHNob2NrcyBvZiB0aGUgc2FtZSBzaXplKS4gUHJpY2UgZm9yZWNhc3RzIGFuZCBpbnRlcnZhbHMgYXJlIG9idGFpbmVkCmZyb20gNSwwMDAgc2ltdWxhdGVkIHJldHVybiBwYXRocy4KClRoZSAqKkFSSU1BICsgd2Vla2RheSoqIG1vZGVsIHJlZ3Jlc3NlcyBkYWlseSBsb2cgcmV0dXJucyAoaW4gJSkgb24gd2Vla2RheQpkdW1taWVzLCB3aXRoIE1vbmRheSBhcyB0aGUgYmFzZWxpbmUgYW5kIEFSTUEgZXJyb3JzOgoKJCQKcl90ID0gXGJldGFfMCArIFxzdW1fe2QgPSBcdGV4dHtUdWV9fV57XHRleHR7RnJpfX0gXGJldGFfZCBEX3tkLHR9ICsgdV90LCBccXF1YWQgdV90IFxzaW0gXHRleHR7QVJNQX0ocCwgcSkKJCQKCkl0cyBwcmljZSBmb3JlY2FzdHMgYW5kIGludGVydmFscyBjb21lIGZyb20gMiwwMDAgYm9vdHN0cmFwcGVkIHNpbXVsYXRlZCBwYXRocy4KCmBgYHtyfQpsaWJyYXJ5KHJ1Z2FyY2gpICAgCgpnYXJjaF9zcGVjIDwtIHVnYXJjaHNwZWMoCiAgdmFyaWFuY2UubW9kZWwgICAgID0gbGlzdChtb2RlbCA9ICJnanJHQVJDSCIsIGdhcmNoT3JkZXIgPSBjKDEsIDEpKSwKICBtZWFuLm1vZGVsICAgICAgICAgPSBsaXN0KGFybWFPcmRlciA9IGMoMCwgMCksIGluY2x1ZGUubWVhbiA9IFRSVUUpLAogIGRpc3RyaWJ1dGlvbi5tb2RlbCA9ICJzdGQiCikKCiMgR0pSLUdBUkNIIHByaWNlIGZvcmVjYXN0cyBmcm9tIHNpbXVsYXRlZCBsb2ctcmV0dXJuIHBhdGhzCmZjX2dhcmNoIDwtIGZ1bmN0aW9uKHksIGgsIG5zaW0gPSA1MDAwKSB7CiAgZml0IDwtIHVnYXJjaGZpdChnYXJjaF9zcGVjLCBkYXRhID0gZGlmZihsb2coeSkpLCBzb2x2ZXIgPSAiaHlicmlkIikKICBpZiAoY29udmVyZ2VuY2UoZml0KSAhPSAwKSBzdG9wKCJHQVJDSCBlc3RpbWF0aW9uIGRpZCBub3QgY29udmVyZ2UiKQogIHNpbSAgIDwtIHVnYXJjaHNpbShmaXQsIG4uc2ltID0gaCwgbS5zaW0gPSBuc2ltLCBzdGFydE1ldGhvZCA9ICJzYW1wbGUiKQogIHJldCAgIDwtIG1hdHJpeChmaXR0ZWQoc2ltKSwgbnJvdyA9IGgpCiAgcGF0aHMgPC0gbWF0cml4KHRhaWwoeSwgMSkgKiBleHAoYXBwbHkocmV0LCAyLCBjdW1zdW0pKSwgbnJvdyA9IGgpCiAgcSAgICAgPC0gZnVuY3Rpb24ocCkgYXBwbHkocGF0aHMsIDEsIHF1YW50aWxlLCBwcm9icyA9IHApCiAgbGlzdChtZXRob2QgPSAiR0pSLUdBUkNIKDEsMSksIFN0dWRlbnQtdCIsCiAgICAgICBtZWFuICAgPSByb3dNZWFucyhwYXRocyksCiAgICAgICBsb3dlciAgPSBjYmluZChxKDAuMTAwKSwgcSgwLjAyNSkpLAogICAgICAgdXBwZXIgID0gY2JpbmQocSgwLjkwMCksIHEoMC45NzUpKSwKICAgICAgIG1vZGVsICA9IGZpdCkKfQoKIyBXZWVrZGF5IGR1bW1pZXMgKE1vbmRheSBpcyB0aGUgYmFzZWxpbmUpCmRvd19tYXRyaXggPC0gZnVuY3Rpb24oZGF0ZXMpIHsKICBkb3cgPC0gZmFjdG9yKGx1YnJpZGF0ZTo6d2RheShkYXRlcywgd2Vla19zdGFydCA9IDEpLCBsZXZlbHMgPSAxOjUsCiAgICAgICAgICAgICAgICBsYWJlbHMgPSBjKCJNb24iLCAiVHVlIiwgIldlZCIsICJUaHUiLCAiRnJpIikpCiAgbW9kZWwubWF0cml4KH4gZG93KVssIC0xLCBkcm9wID0gRkFMU0VdCn0KCiMgV2Vla2RheSByZWdyZXNzaW9uIHdpdGggQVJNQSBlcnJvcnMgb24gbG9nIHJldHVybnMgKCUpOyBuZWVkcyB0aGUgZGF0ZXMgb2YgdGhlCiMgZXN0aW1hdGlvbiB3aW5kb3cgYW5kIG9mIHRoZSB0cmFkaW5nIGRheXMgdG8gYmUgZm9yZWNhc3QKZmNfd2Vla2RheSA8LSBmdW5jdGlvbih5LCBkYXRlcywgbmV3X2RhdGVzLCBuc2ltID0gMjAwMCkgewogIGggICAgIDwtIGxlbmd0aChuZXdfZGF0ZXMpCiAgciAgICAgPC0gMTAwICogZGlmZihsb2coeSkpCiAgZml0ICAgPC0gYXV0by5hcmltYShyLCB4cmVnID0gZG93X21hdHJpeChkYXRlc1stMV0pLCBkID0gMCkKICBzaW1zICA8LSByZXBsaWNhdGUobnNpbSwgYXMubnVtZXJpYyhzaW11bGF0ZShmaXQsIG5zaW0gPSBoLCB4cmVnID0gZG93X21hdHJpeChuZXdfZGF0ZXMpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZ1dHVyZSA9IFRSVUUsIGJvb3RzdHJhcCA9IFRSVUUpKSkKICBwYXRocyA8LSBtYXRyaXgodGFpbCh5LCAxKSAqIGV4cChhcHBseShtYXRyaXgoc2ltcywgbnJvdyA9IGgpIC8gMTAwLCAyLCBjdW1zdW0pKSwgbnJvdyA9IGgpCiAgcSAgICAgPC0gZnVuY3Rpb24ocCkgYXBwbHkocGF0aHMsIDEsIHF1YW50aWxlLCBwcm9icyA9IHApCiAgbGlzdChtZXRob2QgPSBzcHJpbnRmKCJXZWVrZGF5ICsgQVJNQSglZCwlZCkgZXJyb3JzIiwgZml0JGFybWFbMV0sIGZpdCRhcm1hWzJdKSwKICAgICAgIG1lYW4gICA9IHJvd01lYW5zKHBhdGhzKSwKICAgICAgIGxvd2VyICA9IGNiaW5kKHEoMC4xMDApLCBxKDAuMDI1KSksCiAgICAgICB1cHBlciAgPSBjYmluZChxKDAuOTAwKSwgcSgwLjk3NSkpLAogICAgICAgbW9kZWwgID0gZml0KQp9CgojIEVhY2ggZnVuY3Rpb24gdGFrZXMgYSBudW1lcmljIHByaWNlIHZlY3RvciB5IGFuZCBmb3JlY2FzdHMgaCB0cmFkaW5nIGRheXMgYWhlYWQKZmNfZnVucyA8LSBsaXN0KAogICJSYW5kb20gd2FsayIgICAgICAgICAgICA9IGZ1bmN0aW9uKHksIGgpIG5haXZlKHRzKHkpLCBoID0gaCwgbGV2ZWwgPSBsdiksCiAgIlJhbmRvbSB3YWxrIHdpdGggZHJpZnQiID0gZnVuY3Rpb24oeSwgaCkgcndmKHRzKHkpLCBkcmlmdCA9IFRSVUUsIGggPSBoLCBsZXZlbCA9IGx2KSwKICAiQVJJTUEiICAgICAgICAgICAgICAgICAgPSBmdW5jdGlvbih5LCBoKSBmb3JlY2FzdChhdXRvLmFyaW1hKHRzKHkpLCBsYW1iZGEgPSAwLCBiaWFzYWRqID0gVFJVRSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaCA9IGgsIGxldmVsID0gbHYpLAogICJFVFMiICAgICAgICAgICAgICAgICAgICA9IGZ1bmN0aW9uKHksIGgpIGZvcmVjYXN0KGV0cyh0cyh5KSwgbGFtYmRhID0gMCwgYmlhc2FkaiA9IFRSVUUpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGggPSBoLCBsZXZlbCA9IGx2KSwKICAiVGhldGEiICAgICAgICAgICAgICAgICAgPSBmdW5jdGlvbih5LCBoKSB0aGV0YWYodHMoeSksIGggPSBoLCBsZXZlbCA9IGx2KSwKICAiVEJBVFMiICAgICAgICAgICAgICAgICAgPSBmdW5jdGlvbih5LCBoKSBmb3JlY2FzdCh0YmF0cyhtc3RzKHksIHNlYXNvbmFsLnBlcmlvZHMgPSBjKDUsIGFubnVhbF9wZXJpb2QpKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB1c2UuYm94LmNveCA9IFRSVUUsIHVzZS5wYXJhbGxlbCA9IEZBTFNFKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBoID0gaCwgbGV2ZWwgPSBsdiksCiAgIk5OQVIiICAgICAgICAgICAgICAgICAgID0gZnVuY3Rpb24oeSwgaCkgZm9yZWNhc3Qobm5ldGFyKHRzKHkpLCBsYW1iZGEgPSAwKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBoID0gaCwgUEkgPSBUUlVFLCBsZXZlbCA9IGx2KSwKICAiR0pSLUdBUkNIIiAgICAgICAgICAgICAgPSBmdW5jdGlvbih5LCBoKSBmY19nYXJjaCh5LCBoKQopCgojIENvbnZlcnQgYSBmb3JlY2FzdCBvYmplY3QgKG9yIGEgc2ltdWxhdGlvbi1iYXNlZCBsaXN0KSBpbnRvIGEgdGlkeSB0aWJibGUKdGlkeV9mYyA8LSBmdW5jdGlvbihmYywgbW9kZWwsIGgpIHsKICBpZiAoaXMubnVsbChmYykpIHsKICAgIHJldHVybih0aWJibGUobW9kZWwgPSBtb2RlbCwgbWV0aG9kID0gTkFfY2hhcmFjdGVyXywgaG9yaXpvbiA9IHNlcV9sZW4oaCksCiAgICAgICAgICAgICAgICAgIG1lYW4gPSBOQV9yZWFsXywgbG84MCA9IE5BX3JlYWxfLCBoaTgwID0gTkFfcmVhbF8sCiAgICAgICAgICAgICAgICAgIGxvOTUgPSBOQV9yZWFsXywgaGk5NSA9IE5BX3JlYWxfKSkKICB9CiAgdGliYmxlKG1vZGVsICAgPSBtb2RlbCwKICAgICAgICAgbWV0aG9kICA9IGlmIChpcy5udWxsKGZjJG1ldGhvZCkpIG1vZGVsIGVsc2UgZmMkbWV0aG9kLAogICAgICAgICBob3Jpem9uID0gc2VxX2xlbihoKSwKICAgICAgICAgbWVhbiAgICA9IGFzLm51bWVyaWMoZmMkbWVhbiksCiAgICAgICAgIGxvODAgICAgPSBhcy5udW1lcmljKGZjJGxvd2VyWywgMV0pLCBoaTgwID0gYXMubnVtZXJpYyhmYyR1cHBlclssIDFdKSwKICAgICAgICAgbG85NSAgICA9IGFzLm51bWVyaWMoZmMkbG93ZXJbLCAyXSksIGhpOTUgPSBhcy5udW1lcmljKGZjJHVwcGVyWywgMl0pKQp9CgojIEZpdCB0aGUgc2VsZWN0ZWQgcHJpY2Utb25seSBtb2RlbHMgb24gcHJpY2UgdmVjdG9yIHk7IGZhaWxlZCBmaXRzIHJldHVybiBOQSByb3dzCnJ1bl9tb2RlbHMgPC0gZnVuY3Rpb24oeSwgaCwgbW9kZWxzID0gbmFtZXMoZmNfZnVucykpIHsKICBmY3MgPC0gbWFwKGZjX2Z1bnNbbW9kZWxzXSwgfiB0cnlDYXRjaCgueCh5LCBoKSwgZXJyb3IgPSBmdW5jdGlvbihlKSBOVUxMKSkKICBpbWFwX2RmcihmY3MsIH4gdGlkeV9mYygueCwgLnksIGgpKQp9CmBgYAoKIyMjIEZvcmVjYXN0cyBmb3IgMiB0byAxMCBOb3ZlbWJlciAyMDI2CgpgYGB7cn0Kc2V0LnNlZWQoMTIzKQpmY19vYmplY3RzIDwtIG1hcChmY19mdW5zLCB+IHRyeUNhdGNoKC54KHRyYWluJGNsb3NlLCBoKSwgZXJyb3IgPSBmdW5jdGlvbihlKSBOVUxMKSkKCiMgVGhlIHdlZWtkYXkgbW9kZWwgYWxzbyBuZWVkcyBkYXRlczsgbGlzdCgpIGtlZXBzIGEgZmFpbGVkIGZpdCBhcyBOVUxMCmZjX29iamVjdHNbIkFSSU1BICsgd2Vla2RheSJdIDwtIGxpc3QoCiAgdHJ5Q2F0Y2goZmNfd2Vla2RheSh0cmFpbiRjbG9zZSwgdHJhaW4kZGF0ZSwgZnV0dXJlX2RhdGVzKSwgZXJyb3IgPSBmdW5jdGlvbihlKSBOVUxMKQopCgpmY19maW5hbCA8LSBpbWFwX2RmcihmY19vYmplY3RzLCB+IHRpZHlfZmMoLngsIC55LCBoKSkgJT4lCiAgbXV0YXRlKGRhdGUgPSBmdXR1cmVfZGF0ZXNbaG9yaXpvbl0pCgojIFNwZWNpZmljYXRpb24gc2VsZWN0ZWQgZm9yIGVhY2ggbW9kZWwKZmNfZmluYWwgJT4lIGRpc3RpbmN0KG1vZGVsLCBtZXRob2QpCmBgYAoKYGBge3J9CmdhcmNoX2ZpdCA8LSBmY19vYmplY3RzW1siR0pSLUdBUkNIIl1dJG1vZGVsCndkX2ZpdCAgICA8LSBmY19vYmplY3RzW1siQVJJTUEgKyB3ZWVrZGF5Il1dJG1vZGVsCgojIEdKUi1HQVJDSCBlc3RpbWF0ZXMsIHBlcnNpc3RlbmNlLCBhbmQgaGFsZi1saWZlIG9mIHZvbGF0aWxpdHkgc2hvY2tzICh0cmFkaW5nIGRheXMpCmlmICghaXMubnVsbChnYXJjaF9maXQpKSB7CiAgcHJpbnQocm91bmQoZ2FyY2hfZml0QGZpdCRtYXRjb2VmLCA0KSkKICBwcmludChjKHBlcnNpc3RlbmNlID0gcGVyc2lzdGVuY2UoZ2FyY2hfZml0KSwgaGFsZl9saWZlX2RheXMgPSBoYWxmbGlmZShnYXJjaF9maXQpKSkKfQoKIyBXZWVrZGF5IHJlZ3Jlc3Npb24gd2l0aCBBUk1BIGVycm9ycyAocmV0dXJucyBpbiAlKQppZiAoIWlzLm51bGwod2RfZml0KSkgcHJpbnQod2RfZml0KQpgYGAKCmBgYHtyfQojIFBvaW50IGZvcmVjYXN0cyBieSBtb2RlbApmY19maW5hbCAlPiUKICBzZWxlY3QoZGF0ZSwgbW9kZWwsIG1lYW4pICU+JQogIHBpdm90X3dpZGVyKG5hbWVzX2Zyb20gPSBtb2RlbCwgdmFsdWVzX2Zyb20gPSBtZWFuKSAlPiUKICBtdXRhdGUoYWNyb3NzKC1kYXRlLCB+IHJvdW5kKC54LCAxKSkpCmBgYAoKYGBge3J9CiMgUG9pbnQgZm9yZWNhc3RzIHdpdGggODAlIGFuZCA5NSUgcHJlZGljdGlvbiBpbnRlcnZhbHMKZmNfZmluYWwgJT4lCiAgc2VsZWN0KG1vZGVsLCBkYXRlLCBtZWFuLCBsbzgwLCBoaTgwLCBsbzk1LCBoaTk1KSAlPiUKICBtdXRhdGUoYWNyb3NzKG1lYW46aGk5NSwgfiByb3VuZCgueCwgMSkpKQpgYGAKCmBgYHtyfQpwbG90X2ZjIDwtIGZ1bmN0aW9uKGZjLCBoaXN0LCBuY29sID0gMikgewogIGZjICAgICAgIDwtIGZjICU+JSBtdXRhdGUobW9kZWwgPSBmYWN0b3IobW9kZWwsIGxldmVscyA9IHVuaXF1ZShtb2RlbCkpKQogIGxhc3Rfb2JzIDwtIHNsaWNlX3RhaWwoaGlzdCwgbiA9IDEpCgogICMgQ29ubmVjdCB0aGUgbGFzdCBvYnNlcnZhdGlvbiB0byBlYWNoIGZvcmVjYXN0IGxpbmUKICBicmlkZ2UgPC0gZGlzdGluY3QoZmMsIG1vZGVsKSAlPiUKICAgIG11dGF0ZShkYXRlID0gbGFzdF9vYnMkZGF0ZSwgbWVhbiA9IGxhc3Rfb2JzJGNsb3NlLAogICAgICAgICAgIGxvODAgPSBtZWFuLCBoaTgwID0gbWVhbiwgbG85NSA9IG1lYW4sIGhpOTUgPSBtZWFuKQoKICBmY19saW5lICA8LSBiaW5kX3Jvd3MoZmMsIGJyaWRnZSkgJT4lIGFycmFuZ2UobW9kZWwsIGRhdGUpCiAgaGlzdF9yZXAgPC0gY3Jvc3Nfam9pbihkaXN0aW5jdChmYywgbW9kZWwpLCBzZWxlY3QoaGlzdCwgZGF0ZSwgY2xvc2UpKQoKICBnZ3Bsb3QoKSArCiAgICBnZW9tX3JpYmJvbihkYXRhID0gZmNfbGluZSwgYWVzKGRhdGUsIHltaW4gPSBsbzk1LCB5bWF4ID0gaGk5NSwgZmlsbCA9ICI5NSUgaW50ZXJ2YWwiKSwKICAgICAgICAgICAgICAgIGFscGhhID0gMC4zNSkgKwogICAgZ2VvbV9yaWJib24oZGF0YSA9IGZjX2xpbmUsIGFlcyhkYXRlLCB5bWluID0gbG84MCwgeW1heCA9IGhpODAsIGZpbGwgPSAiODAlIGludGVydmFsIiksCiAgICAgICAgICAgICAgICBhbHBoYSA9IDAuNTUpICsKICAgIGdlb21fbGluZShkYXRhID0gaGlzdF9yZXAsIGFlcyhkYXRlLCBjbG9zZSwgY29sb3VyID0gIkFjdHVhbCIpLCBsaW5ld2lkdGggPSAwLjUpICsKICAgIGdlb21fbGluZShkYXRhID0gZmNfbGluZSwgYWVzKGRhdGUsIG1lYW4sIGNvbG91ciA9ICJQb2ludCBmb3JlY2FzdCIpLCBsaW5ld2lkdGggPSAwLjcpICsKICAgIGdlb21fcG9pbnQoZGF0YSA9IGZjLCBhZXMoZGF0ZSwgbWVhbiwgY29sb3VyID0gIlBvaW50IGZvcmVjYXN0IiksIHNpemUgPSAxLjIpICsKICAgIGZhY2V0X3dyYXAofiBtb2RlbCwgbmNvbCA9IG5jb2wpICsKICAgIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGMoIjk1JSBpbnRlcnZhbCIgPSAiI2M2ZDllYyIsICI4MCUgaW50ZXJ2YWwiID0gIiM4ZmIzZDkiKSwKICAgICAgICAgICAgICAgICAgICAgIGJyZWFrcyA9IGMoIjgwJSBpbnRlcnZhbCIsICI5NSUgaW50ZXJ2YWwiKSkgKwogICAgc2NhbGVfY29sb3VyX21hbnVhbCh2YWx1ZXMgPSBjKCJBY3R1YWwiID0gImdyZXkyNSIsICJQb2ludCBmb3JlY2FzdCIgPSBwYWxbWyJvcmFuZ2UiXV0pKSArCiAgICBzY2FsZV94X2RhdGUoZGF0ZV9icmVha3MgPSAiMiB3ZWVrcyIsIGRhdGVfbGFiZWxzID0gIiVkICViIikgKwogICAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscyA9IGNvbW1hKSArCiAgICBsYWJzKHggPSBOVUxMLCB5ID0gIkluZGV4IGxldmVsIikgKwogICAgdGhlbWVfaWhzZygpCn0KCiMgVXNlIHRoZSBzZWxlY3RlZCBzcGVjaWZpY2F0aW9uIGFzIHRoZSBwYW5lbCB0aXRsZSB3aGVyZSBpdCBpcyBpbmZvcm1hdGl2ZQpmY19wbG90X2RhdGEgPC0gZmNfZmluYWwgJT4lCiAgbXV0YXRlKG1vZGVsID0gaWZfZWxzZShtb2RlbCAlaW4lIGMoIkFSSU1BIiwgIkVUUyIsICJUQkFUUyIsICJOTkFSIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiR0pSLUdBUkNIIiwgIkFSSU1BICsgd2Vla2RheSIpLAogICAgICAgICAgICAgICAgICAgICAgICAgY29hbGVzY2UobWV0aG9kLCBtb2RlbCksIG1vZGVsKSkKCnAgPC0gcGxvdF9mYyhmY19wbG90X2RhdGEsIHNsaWNlX3RhaWwodHJhaW4sIG4gPSA0MCksIG5jb2wgPSAzKSArCiAgbGFicyh0aXRsZSA9ICJKS1NFIENsb3NpbmcgUHJpY2UgRm9yZWNhc3RzLCAyIHRvIDEwIE5vdmVtYmVyIDIwMjYiLAogICAgICAgc3VidGl0bGUgPSAiTGFzdCA0MCB0cmFkaW5nIGRheXMgd2l0aCBwb2ludCBmb3JlY2FzdHMgYW5kIDgwJSAvIDk1JSBwcmVkaWN0aW9uIGludGVydmFscyIsCiAgICAgICBjYXB0aW9uID0gc291cmNlX2xhYmVsKQoKaGRfcGxvdChwLCAiMDNfZm9yZWNhc3RzX2J5X21vZGVsIiwgd2lkdGggPSAxMCwgaGVpZ2h0ID0gOSkKYGBgCgoKIyMjIFNpbXVsYXRlZCBwcmljZSBwYXRocwoKVGhlIGZpZ3VyZSBzaG93cyAzMCBvZiAyLDAwMCBwYXRocyBzaW11bGF0ZWQgZnJvbSB0aGUgZml0dGVkIEdKUi1HQVJDSCBtb2RlbC4KSW5kaXZpZHVhbCBwYXRocyBtb3ZlIHVwIGFuZCBkb3duLCBidXQgYmVjYXVzZSB0aGUgZGlyZWN0aW9uIG9mIGVhY2ggbW92ZSBpcwp1bnByZWRpY3RhYmxlLCB0aGVpciBhdmVyYWdlIGlzIG5lYXJseSBmbGF0LiBUaGUgYmFuZHMgcmVmbGVjdCB0aGUgY3VycmVudApsZXZlbCBvZiB2b2xhdGlsaXR5LgoKYGBge3J9CmlmIChpcy5udWxsKGdhcmNoX2ZpdCkpIHN0b3AoIlRoZSBHSlItR0FSQ0ggZml0IGlzIG5vdCBhdmFpbGFibGUiKQoKc2V0LnNlZWQoMTIzKQpuX3BhdGhzICAgIDwtIDIwMDAKbGFzdF9jbG9zZSA8LSB0YWlsKHRyYWluJGNsb3NlLCAxKQoKc2ltICAgPC0gdWdhcmNoc2ltKGdhcmNoX2ZpdCwgbi5zaW0gPSBoLCBtLnNpbSA9IG5fcGF0aHMsIHN0YXJ0TWV0aG9kID0gInNhbXBsZSIpCnJldCAgIDwtIG1hdHJpeChmaXR0ZWQoc2ltKSwgbnJvdyA9IGgpCnBhdGhzIDwtIHJiaW5kKHJlcChsYXN0X2Nsb3NlLCBuX3BhdGhzKSwKICAgICAgICAgICAgICAgbGFzdF9jbG9zZSAqIGV4cChhcHBseShyZXQsIDIsIGN1bXN1bSkpKQpjb2xuYW1lcyhwYXRocykgPC0gcGFzdGUwKCJwYXRoXyIsIHNlcV9sZW4obl9wYXRocykpCgpwYXRoX3RibCA8LSBhc190aWJibGUocGF0aHMpICU+JQogIG11dGF0ZShkYXRlID0gYyhtYXgodHJhaW4kZGF0ZSksIGZ1dHVyZV9kYXRlcykpICU+JQogIHBpdm90X2xvbmdlcigtZGF0ZSwgbmFtZXNfdG8gPSAicGF0aCIsIHZhbHVlc190byA9ICJjbG9zZSIpCgpiYW5kX3RibCA8LSBwYXRoX3RibCAlPiUKICBncm91cF9ieShkYXRlKSAlPiUKICBzdW1tYXJpc2UobWVhbiA9IG1lYW4oY2xvc2UpLAogICAgICAgICAgICBsbzgwID0gcXVhbnRpbGUoY2xvc2UsIDAuMTAwKSwgaGk4MCA9IHF1YW50aWxlKGNsb3NlLCAwLjkwMCksCiAgICAgICAgICAgIGxvOTUgPSBxdWFudGlsZShjbG9zZSwgMC4wMjUpLCBoaTk1ID0gcXVhbnRpbGUoY2xvc2UsIDAuOTc1KSwKICAgICAgICAgICAgLmdyb3VwcyA9ICJkcm9wIikKCnNob3duIDwtIHNhbXBsZShjb2xuYW1lcyhwYXRocyksIDMwKQoKcCA8LSBnZ3Bsb3QoKSArCiAgZ2VvbV9yaWJib24oZGF0YSA9IGJhbmRfdGJsLCBhZXMoZGF0ZSwgeW1pbiA9IGxvOTUsIHltYXggPSBoaTk1KSwKICAgICAgICAgICAgICBmaWxsID0gIiNjNmQ5ZWMiLCBhbHBoYSA9IDAuNykgKwogIGdlb21fcmliYm9uKGRhdGEgPSBiYW5kX3RibCwgYWVzKGRhdGUsIHltaW4gPSBsbzgwLCB5bWF4ID0gaGk4MCksCiAgICAgICAgICAgICAgZmlsbCA9ICIjOGZiM2Q5IiwgYWxwaGEgPSAwLjcpICsKICBnZW9tX2xpbmUoZGF0YSA9IGZpbHRlcihwYXRoX3RibCwgcGF0aCAlaW4lIHNob3duKSwKICAgICAgICAgICAgYWVzKGRhdGUsIGNsb3NlLCBncm91cCA9IHBhdGgpLAogICAgICAgICAgICBjb2xvdXIgPSBwYWxbWyJuYXZ5Il1dLCBhbHBoYSA9IDAuNCwgbGluZXdpZHRoID0gMC4zNSkgKwogIGdlb21fbGluZShkYXRhID0gc2xpY2VfdGFpbCh0cmFpbiwgbiA9IDQwKSwgYWVzKGRhdGUsIGNsb3NlKSwKICAgICAgICAgICAgY29sb3VyID0gImdyZXkyNSIsIGxpbmV3aWR0aCA9IDAuNikgKwogIGdlb21fbGluZShkYXRhID0gYmFuZF90YmwsIGFlcyhkYXRlLCBtZWFuKSwgY29sb3VyID0gcGFsW1sib3JhbmdlIl1dLCBsaW5ld2lkdGggPSAxKSArCiAgc2NhbGVfeF9kYXRlKGRhdGVfYnJlYWtzID0gIjIgd2Vla3MiLCBkYXRlX2xhYmVscyA9ICIlZCAlYiIpICsKICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gY29tbWEpICsKICBsYWJzKHRpdGxlID0gIlNpbXVsYXRlZCBKS1NFIFByaWNlIFBhdGhzLCAyIHRvIDEwIE5vdmVtYmVyIDIwMjYiLAogICAgICAgc3VidGl0bGUgPSAiMzAgb2YgMiwwMDAgR0pSLUdBUkNIIHBhdGhzIChibHVlKSwgdGhlaXIgbWVhbiAob3JhbmdlKSwgYW5kIDgwJSAvIDk1JSBiYW5kcyIsCiAgICAgICB4ID0gTlVMTCwgeSA9ICJJbmRleCBsZXZlbCIsIGNhcHRpb24gPSBzb3VyY2VfbGFiZWwpICsKICB0aGVtZV9paHNnKCkKCmhkX3Bsb3QocCwgIjA0X3NpbXVsYXRlZF9wYXRocyIpCmBgYAoKIyMjIFJvbGxpbmctb3JpZ2luIGV2YWx1YXRpb24KCkEgc2luZ2xlIGhvbGRvdXQgb2YgYSBmZXcgZGF5cyBpcyB0b28gbm9pc3kgdG8gcmFuayBtb2RlbHMuIEVhY2ggbW9kZWwgaXMKcmUtZXN0aW1hdGVkIG9uIGEgcm9sbGluZyB3aW5kb3cgb2YgdGhlIHNhbWUgbGVuZ3RoIGFzIHRoZSBmaW5hbCBlc3RpbWF0aW9uCndpbmRvdywgYXQgZm9yZWNhc3Qgb3JpZ2lucyBzcGFjZWQgNSB0cmFkaW5nIGRheXMgYXBhcnQgb3ZlciB0aGUgbGFzdCAyNTAKdHJhZGluZyBkYXlzLiBUaGUgQVJJTUEgKyB3ZWVrZGF5IG1vZGVsIHVzZXMgdGhlIGRhdGVzIG9mIGVhY2ggd2luZG93IGFuZCB0aGUKYWN0dWFsIHRyYWRpbmcgZGF0ZXMgdGhhdCBmb2xsb3cgZWFjaCBvcmlnaW4sIHdoaWNoIGFyZSBrbm93biBpbiBhZHZhbmNlIGZyb20KdGhlIElEWCBjYWxlbmRhci4gRXJyb3JzIGFyZSBhdmVyYWdlZCBvdmVyIGFsbCBvcmlnaW5zIGFuZCBob3Jpem9ucy4gQ292ZXJhZ2UKY2xvc2UgdG8gMC44MCBhbmQgMC45NSBpbmRpY2F0ZXMgd2VsbC1jYWxpYnJhdGVkIHByZWRpY3Rpb24gaW50ZXJ2YWxzLiBUQkFUUyBhbmQKTk5BUiBhcmUgb25seSBpbmNsdWRlZCB3aGVuIGBydW5fc2xvd19iYWNrdGVzdCA9IFRSVUVgLiAKCmBgYHtyfQpidF9tb2RlbHMgPC0gYygiUmFuZG9tIHdhbGsiLCAiUmFuZG9tIHdhbGsgd2l0aCBkcmlmdCIsICJBUklNQSIsICJFVFMiLCAiVGhldGEiLAogICAgICAgICAgICAgICAiR0pSLUdBUkNIIiwgIkFSSU1BICsgd2Vla2RheSIpCmlmIChydW5fc2xvd19iYWNrdGVzdCkgYnRfbW9kZWxzIDwtIGMoYnRfbW9kZWxzLCAiVEJBVFMiLCAiTk5BUiIpCgp5X2FsbCAgIDwtIGloc2ckY2xvc2UKbl93aW4gICA8LSBucm93KHRyYWluKSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIHNhbWUgd2luZG93IGxlbmd0aCBhcyB0aGUgZmluYWwgbW9kZWxzCm9yaWdpbnMgPC0gc2VxKGxlbmd0aCh5X2FsbCkgLSAyNTAsIGxlbmd0aCh5X2FsbCkgLSBoLCBieSA9IDUpCgpzZXQuc2VlZCgxMjMpCmJ0IDwtIG1hcF9kZnIob3JpZ2lucywgZnVuY3Rpb24obykgewogIGlkeCA8LSAobyAtIG5fd2luICsgMSk6bwoKICAjIE1vZGVscyB0aGF0IHVzZSBwcmljZXMgb25seQogIGZjX3ByaWNlIDwtIHJ1bl9tb2RlbHMoeV9hbGxbaWR4XSwgaCwgbW9kZWxzID0gaW50ZXJzZWN0KGJ0X21vZGVscywgbmFtZXMoZmNfZnVucykpKQoKICAjIFdlZWtkYXkgbW9kZWw6IGFsc28gbmVlZHMgdGhlIHdpbmRvdyBkYXRlcyBhbmQgdGhlIG5leHQgaCB0cmFkaW5nIGRhdGVzCiAgd2Rfb2JqIDwtIHRyeUNhdGNoKGZjX3dlZWtkYXkoeV9hbGxbaWR4XSwgaWhzZyRkYXRlW2lkeF0sIGloc2ckZGF0ZVtvICsgc2VxX2xlbihoKV0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbnNpbSA9IDEwMDApLAogICAgICAgICAgICAgICAgICAgICBlcnJvciA9IGZ1bmN0aW9uKGUpIE5VTEwpCgogIGJpbmRfcm93cyhmY19wcmljZSwgdGlkeV9mYyh3ZF9vYmosICJBUklNQSArIHdlZWtkYXkiLCBoKSkgJT4lCiAgICBtdXRhdGUob3JpZ2luICAgICAgPSBvLAogICAgICAgICAgIG9yaWdpbl9kYXRlID0gaWhzZyRkYXRlW29dLAogICAgICAgICAgIGFjdHVhbCAgICAgID0geV9hbGxbbyArIGhvcml6b25dKQp9KQoKc2F2ZVJEUyhidCwgImJhY2t0ZXN0X3Jlc3VsdHMucmRzIikgICAKYGBgCgpgYGB7cn0KYnQgJT4lCiAgZ3JvdXBfYnkobW9kZWwpICU+JQogIHN1bW1hcmlzZSgKICAgIFJNU0UgICAgICAgICAgID0gc3FydChtZWFuKChhY3R1YWwgLSBtZWFuKV4yLCBuYS5ybSA9IFRSVUUpKSwKICAgIE1BRSAgICAgICAgICAgID0gbWVhbihhYnMoYWN0dWFsIC0gbWVhbiksIG5hLnJtID0gVFJVRSksCiAgICBNQVBFICAgICAgICAgICA9IDEwMCAqIG1lYW4oYWJzKChhY3R1YWwgLSBtZWFuKSAvIGFjdHVhbCksIG5hLnJtID0gVFJVRSksCiAgICBgQ292ZXJhZ2UgODAlYCA9IG1lYW4oYWN0dWFsID49IGxvODAgJiBhY3R1YWwgPD0gaGk4MCwgbmEucm0gPSBUUlVFKSwKICAgIGBDb3ZlcmFnZSA5NSVgID0gbWVhbihhY3R1YWwgPj0gbG85NSAmIGFjdHVhbCA8PSBoaTk1LCBuYS5ybSA9IFRSVUUpLAogICAgYEZhaWxlZCBmaXRzYCAgPSBzdW0oaXMubmEobWVhbikpIC8gaCwKICAgIC5ncm91cHMgPSAiZHJvcCIKICApICU+JQogIG11dGF0ZShgUk1TRSB2cyBSV2AgPSBSTVNFIC8gUk1TRVttb2RlbCA9PSAiUmFuZG9tIHdhbGsiXSkgJT4lCiAgcmVsb2NhdGUoYFJNU0UgdnMgUldgLCAuYWZ0ZXIgPSBSTVNFKSAlPiUKICBhcnJhbmdlKFJNU0UpICU+JQogIG11dGF0ZShhY3Jvc3Mod2hlcmUoaXMubnVtZXJpYyksIH4gcm91bmQoLngsIDMpKSkKYGBgCgpgYGB7cn0Kcm1zZV9oIDwtIGJ0ICU+JQogIGdyb3VwX2J5KG1vZGVsLCBob3Jpem9uKSAlPiUKICBzdW1tYXJpc2UoUk1TRSA9IHNxcnQobWVhbigoYWN0dWFsIC0gbWVhbileMiwgbmEucm0gPSBUUlVFKSksIC5ncm91cHMgPSAiZHJvcCIpICU+JQogIGdyb3VwX2J5KGhvcml6b24pICU+JQogIG11dGF0ZShyZWxfcm1zZSA9IFJNU0UgLyBSTVNFW21vZGVsID09ICJSYW5kb20gd2FsayJdKSAlPiUKICB1bmdyb3VwKCkgJT4lCiAgbXV0YXRlKG1vZGVsID0gZmFjdG9yKG1vZGVsLCBsZXZlbHMgPSBidF9tb2RlbHMpKQoKIyBOaW5lIGRpc3RpbmN0IGNvbG91cnMsIGVub3VnaCBmb3IgYWxsIG1vZGVscyB3aGVuIHJ1bl9zbG93X2JhY2t0ZXN0ID0gVFJVRQptb2RlbF9jb2xzIDwtIGMoIiMxZjRlNzkiLCAiI2UwN2IzOSIsICIjMmU4YjU3IiwgIiNjMDM5MmIiLCAiIzdiMzI5NCIsCiAgICAgICAgICAgICAgICAiIzhjNmQzMSIsICIjMTdiZWNmIiwgIiNlMzc3YzIiLCAiZ3JleTQwIikKCnAgPC0gZ2dwbG90KHJtc2VfaCwgYWVzKGhvcml6b24sIHJlbF9ybXNlLCBjb2xvdXIgPSBtb2RlbCkpICsKICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQgPSAxLCBsaW5ldHlwZSA9ICJkYXNoZWQiLCBjb2xvdXIgPSAiZ3JleTQwIikgKwogIGdlb21fbGluZShsaW5ld2lkdGggPSAwLjgpICsKICBnZW9tX3BvaW50KHNpemUgPSAxLjgpICsKICBzY2FsZV9jb2xvdXJfbWFudWFsKHZhbHVlcyA9IG1vZGVsX2NvbHMpICsKICBzY2FsZV94X2NvbnRpbnVvdXMoYnJlYWtzID0gc2VxX2xlbihoKSkgKwogIGd1aWRlcyhjb2xvdXIgPSBndWlkZV9sZWdlbmQobnJvdyA9IDIpKSArCiAgbGFicyh0aXRsZSA9ICJGb3JlY2FzdCBBY2N1cmFjeSBSZWxhdGl2ZSB0byB0aGUgUmFuZG9tIFdhbGsiLAogICAgICAgc3VidGl0bGUgPSAiUk1TRSByYXRpbyBieSBob3Jpem9uIGFjcm9zcyBhbGwgb3JpZ2luczsgdmFsdWVzIGJlbG93IDEgYmVhdCB0aGUgcmFuZG9tIHdhbGsiLAogICAgICAgeCA9ICJGb3JlY2FzdCBob3Jpem9uICh0cmFkaW5nIGRheXMpIiwgeSA9ICJSZWxhdGl2ZSBSTVNFIiwKICAgICAgIGNhcHRpb24gPSBzb3VyY2VfbGFiZWwpICsKICB0aGVtZV9paHNnKCkKCmhkX3Bsb3QocCwgIjA1X3JlbGF0aXZlX3Jtc2UiLCBoZWlnaHQgPSA0LjUpCmBgYAoKRGllYm9sZOKAk01hcmlhbm8gdGVzdHMgY29tcGFyZSBvbmUtc3RlcC1haGVhZCBzcXVhcmVkIGVycm9ycyBvZiBlYWNoIG1vZGVsIHdpdGgKdGhlIHJhbmRvbSB3YWxrLiBBIG5lZ2F0aXZlIHN0YXRpc3RpYyBtZWFucyB0aGUgbW9kZWwgaGFzIHNtYWxsZXIgZXJyb3JzOyBhCnAtdmFsdWUgYmVsb3cgMC4wNSBpbmRpY2F0ZXMgdGhhdCB0aGUgZGlmZmVyZW5jZSBpcyBzdGF0aXN0aWNhbGx5IHNpZ25pZmljYW50LgoKYGBge3J9CmUxIDwtIGJ0ICU+JQogIGZpbHRlcihob3Jpem9uID09IDEpICU+JQogIHRyYW5zbXV0ZShvcmlnaW4sIG1vZGVsLCBlcnJvciA9IGFjdHVhbCAtIG1lYW4pICU+JQogIHBpdm90X3dpZGVyKG5hbWVzX2Zyb20gPSBtb2RlbCwgdmFsdWVzX2Zyb20gPSBlcnJvcikKCm1hcF9kZnIoc2V0ZGlmZihidF9tb2RlbHMsICJSYW5kb20gd2FsayIpLCBmdW5jdGlvbihtKSB7CiAgb2sgICA8LSBjb21wbGV0ZS5jYXNlcyhlMVtbbV1dLCBlMVtbIlJhbmRvbSB3YWxrIl1dKQogIHRlc3QgPC0gZG0udGVzdChlMVtbbV1dW29rXSwgZTFbWyJSYW5kb20gd2FsayJdXVtva10sIGggPSAxLCBwb3dlciA9IDIpCiAgdGliYmxlKG1vZGVsID0gbSwgRE1fc3RhdGlzdGljID0gdW5uYW1lKHRlc3Qkc3RhdGlzdGljKSwgcF92YWx1ZSA9IHRlc3QkcC52YWx1ZSkKfSkgJT4lCiAgbXV0YXRlKGFjcm9zcyh3aGVyZShpcy5udW1lcmljKSwgfiByb3VuZCgueCwgMykpKQpgYGA=