Simulating Data
Applying a similar code from these notes I simulate a time series that exhibits a GARCH-like data over 1000 periods.
library(fGarch)
seed <- 17
sim_garch <- function(theta,n) {
# set the model parameters
con <- theta[1]
w <- theta[2]
a <- theta[3]
b <- theta[4]
sigma_t <- sqrt(w/(1-a-b)) # start with initial volatility
ht_seq <- numeric()
R_seq <- numeric()
set.seed(seed)
for(i in 1:n) {
R_t1 <- rnorm(1,con,sigma_t)
R_seq <- c(R_seq,R_t1)
ht1 <- w + a*(R_t1^2) + b*sigma_t^2
ht_seq <- c(ht_seq,ht1)
sigma_t <- sqrt(ht1)
}
list(R_t = R_seq,h_t = ht_seq)
}
# set the number simulated returns
n <- 10^3
con <- 0
w <- 0.1
a <- 0.2
b <- 0.5
par_0 <- c(con,w,a,b)
sim1 <- sim_garch(par_0,n)
h_t <- sim1$h_t
R_t <- sim1$R_t
plot(sim1$h_t, type = "l", ylab = expression(h[t]))
grid(10)

Main Objective
Suppose we would like to forecast the volatility over the last 10% periods on a dynamic basis. To do so, let’s first split the data:
N_test <- 0.1*n
index <- 1:n
index_test <- (n - N_test + 1):n
index_train <- index[!index %in% index_test]
h_train <- h_t[index_train]
h_test <- h_t[index_test]
R_train <- R_t[index_train]
R_test <- R_t[index_test]
The question remains how can we forecast the h_test process given the h_train? To answer this, I will consider two approaches. One is a model driven (GARCH) and the other is data driven (ML).
Model Based Forecast
A simple approach to forecast future volatility is to build the volatility term structure using GARCH. To do so, we need to calibrate a GARCH model, which requires an estimation procedure using actual data. The volatility term structure is constructed to evaluate the convergence of the current volatility to the long-term one. In other words, it helps us to assess whether the current volatility level is greater or less than the long-term one. Also, it answers the question how long it would take the current volatility to converge back to the long-term level.
For back-testing, it is more realistic to construct the forecast on a dynamic basis. This can be achieved by considering the realized volatility as an update in the next period volatility forecasting procedure. This can be achieved as follows
g_train <- garchFit(formula = ~ garch(1,1), data = R_train ,trace = F)
theta_hat <- g_train@fit$coef
gamma <- theta_hat[3] + theta_hat[4]
w <- theta_hat[2]
h_for <- h_t[length(index_train)]
h_fit <- numeric()
for(i in 1:length(index_test)){
h_next <- w + gamma*h_t[length(index_train)+i-1]
h_fit <- c(h_fit,h_next)
}
{
plot(h_test~index_test, type = "l", ylab = expression(h[t]), col = 1)
abline(h = w/(1-gamma),lty = 2)
lines(h_fit~index_test,col = 3,lty = 1)
legend("topleft",c("True","GARCH"), col = c(1,3))
grid(10)
}

To see how the two compare, we can plot the realized versus the forecast.
{
plot(h_test~h_fit,pch = 20,cex = 0.5)
abline(a=0,b=1,lty = 2)
grid(10)
}

In terms of regression, we have
lm_garch <- lm(h_test~h_fit)
stargazer::stargazer(lm_garch,type = "html",covariate.labels = "GARCH Forecast",dep.var.labels = "Actual Volatility")
|
|
|
|
Dependent variable:
|
|
|
|
|
|
Actual Volatility
|
|
|
|
GARCH Forecast
|
0.970***
|
|
|
(0.086)
|
|
|
|
|
Constant
|
-0.017
|
|
|
(0.028)
|
|
|
|
|
|
|
Observations
|
100
|
|
R2
|
0.562
|
|
Adjusted R2
|
0.558
|
|
Residual Std. Error
|
0.066 (df = 98)
|
|
F Statistic
|
125.816*** (df = 1; 98)
|
|
|
|
Note:
|
p<0.1; p<0.05; p<0.01
|
We observe that the GARCH forecasts closely mimic the actual test data, i.e. intercept of zero, slope roughly 1, and \(R^2\) of 56%. Nonetheless, the main issue here is that the data set, by construction, follows a GARCH model. Hence, this should not come as a surprise. The more challenging and realistic task is whether the data in real-life is stationary and obeys to such data generating function.
Machine Learning Approach
A data-driven approach does not possess information about the underlying process of the data. Hence, an ML approach would learn the volatility behavior from the data without imposing a structural model. The task here is to investigate the capability of the ML to map the data into a volatility forecast and compare it with respect to its benchmark, the GARCH model.
I use a similar code from my previous notes for implementation of ML algorithms. In this case, I focus on the support vector machines (SVM) with a radial kernel. To do so, my feature space is constructed using the current process itself (squared) and the current log-realized volatility. The outcome variable, on the other hand, is given by the next period log-realized volatility. Note that we use a log transformation, followed by an exponential one, to make sure the fitted volatilities are non-negative.
Similar to the previous analysis, we split the data into in-sample and out-of-sample sets. Additionally, for a more efficient implementation, I refer to the doParallel package for parallel computing on an 8-core laptop as demonstrated below:
library(caret)
library(doParallel)
ds <- na.omit(data.frame(h = log(h_t), r = R_t^2, y = log(c(h_t[-1],NA)) ))
index <- 1:nrow(ds)
index_train2 <- index_train
index_train2 <- index_train2[-length(index_train2)] # avoid the look ahead observation
index_test <- index[!index %in% index_train2]
ds_train <- ds[index_train,]
ds_test <- ds[index_test,]
model_i <- "svmRadial"
trctrl <- trainControl(method = "repeatedcv", number = 10, repeats = 5)
cl <- makePSOCKcluster(8)
registerDoParallel(cl)
train_model <- train(y~ h + r, data = ds_train, method = model_i,
trControl=trctrl,
preProcess = c("center", "scale"),
tuneLength = 10)
stopCluster(cl)
Given the trained model, we forecast the next period using the test dataset and use the exponential transformation to retrieve the volatility level. As a summary, we plot altogether:
h_ml <- exp(predict(train_model,ds_test))
{
plot(h_test~index_test,type = "l")
lines(h_ml~index_test,col = 2)
lines(h_fit~index_test,col = 3)
legend("topleft",c("True","ML","GARCH"))
grid(10)
}

From the above graph, we observe that the ML algorithm closely mimics the GARCH model in terms of forecasts. Since the data in our case is stationary, learning from historical data should be relevant for future applications. At the same time, we discern that the ML algorithm is able to uncover the auto-regressive component in the volatility process.
In terms of regression, we note the ML forecast closely corresponds to the GARCH statistics, with a slightly smaller \(R^2\). This can be inferred from the following regression:
lm_ml <- lm(h_test~h_ml)
stargazer::stargazer(list(lm_ml,lm_garch),type = "html",covariate.labels = c("ML Forecast","GARCH Forecast"),dep.var.labels = "Actual Volatility")
|
|
|
|
Dependent variable:
|
|
|
|
|
|
Actual Volatility
|
|
|
(1)
|
(2)
|
|
|
|
ML Forecast
|
1.097***
|
|
|
|
(0.103)
|
|
|
|
|
|
|
GARCH Forecast
|
|
0.970***
|
|
|
|
(0.086)
|
|
|
|
|
|
Constant
|
-0.014
|
-0.017
|
|
|
(0.029)
|
(0.028)
|
|
|
|
|
|
|
|
Observations
|
100
|
100
|
|
R2
|
0.537
|
0.562
|
|
Adjusted R2
|
0.532
|
0.558
|
|
Residual Std. Error (df = 98)
|
0.068
|
0.066
|
|
F Statistic (df = 1; 98)
|
113.545***
|
125.816***
|
|
|
|
Note:
|
p<0.1; p<0.05; p<0.01
|
LS0tCnRpdGxlOiAiVm9sYXRpbGl0eSBNb2RlbHMgdmlhIE1hY2hpbmUgTGVhcm5pbmciCiNvdXRwdXQ6IHJtYXJrZG93bjo6Z2l0aHViX2RvY3VtZW50Cm91dHB1dDoKICBodG1sX25vdGVib29rOiBkZWZhdWx0CiAgcGRmX2RvY3VtZW50OiBkZWZhdWx0CmF1dGhvcjogTWFqZWVkIFNpbWFhbgpkYXRlOiBEZWMgMjgsIDIwMTkKZmlnX3dpZHRoOiA1MAotLS0KCiMgT3ZlcnZpZXcKSW4gb25lIG9mIG15IGVhcmxpZXIgbm90ZXMgb24gR0FSQ0ggbW9kZWxzIChzZWUgbGluayBbaGVyZV0oaHR0cHM6Ly9ycHVicy5jb20vc2ltYWFuODQvZ2FyY2gpKSwgSSBkZW1vbnN0cmF0ZWQgaG93IHRvIHNpbXVsYXRlLCBlc3RpbWF0ZSwgYW5kIGZvcmVjYXN0IHZvbGF0aWxpdHkgbW9kZWxzLiBJbiB0aGlzIHZpZ25ldHRlLCBJIHdvdWxkIGxpa2UgdG8gZGVtb25zdHJhdGUgaG93IHRvIGRlcGxveSBhIG1hY2hpbmUgbGVhcm5pbmcgKGhlbmNlZm9ydGggTUwpIGFwcHJvYWNoIGZvciB2b2xhdGlsaXR5IGZvcmVjYXN0aW5nLiAKCgojIFNpbXVsYXRpbmcgRGF0YQpBcHBseWluZyBhIHNpbWlsYXIgY29kZSBmcm9tIHRoZXNlIFtub3Rlc10oaHR0cHM6Ly9ycHVicy5jb20vc2ltYWFuODQvZ2FyY2gpIEkgc2ltdWxhdGUgYSB0aW1lIHNlcmllcyB0aGF0IGV4aGliaXRzIGEgR0FSQ0gtbGlrZSBkYXRhIG92ZXIgMTAwMCBwZXJpb2RzLiAKYGBge3IsZmlnLmFsaWduPSdjZW50ZXInLG1lc3NhZ2U9RkFMU0V9CmxpYnJhcnkoZkdhcmNoKQoKc2VlZCA8LSAxNwoKc2ltX2dhcmNoIDwtIGZ1bmN0aW9uKHRoZXRhLG4pIHsKICAKICAjIHNldCB0aGUgbW9kZWwgcGFyYW1ldGVycwogIGNvbiA8LSB0aGV0YVsxXQogIHcgPC0gdGhldGFbMl0KICBhIDwtIHRoZXRhWzNdCiAgYiA8LSB0aGV0YVs0XQogIAogIHNpZ21hX3QgPC0gc3FydCh3LygxLWEtYikpICMgc3RhcnQgd2l0aCBpbml0aWFsIHZvbGF0aWxpdHkgCiAgaHRfc2VxIDwtIG51bWVyaWMoKQogIFJfc2VxIDwtIG51bWVyaWMoKQogIAogIHNldC5zZWVkKHNlZWQpCiAgCiAgZm9yKGkgaW4gMTpuKSB7CiAgICBSX3QxIDwtIHJub3JtKDEsY29uLHNpZ21hX3QpCiAgICBSX3NlcSA8LSBjKFJfc2VxLFJfdDEpCiAgICBodDEgPC0gdyArIGEqKFJfdDFeMikgKyBiKnNpZ21hX3ReMgogICAgaHRfc2VxIDwtIGMoaHRfc2VxLGh0MSkKICAgIHNpZ21hX3QgPC0gc3FydChodDEpCiAgfQogIAogIGxpc3QoUl90ID0gUl9zZXEsaF90ID0gaHRfc2VxKQp9CgojIHNldCB0aGUgbnVtYmVyIHNpbXVsYXRlZCByZXR1cm5zCm4gPC0gMTBeMwoKY29uIDwtIDAKdyA8LSAwLjEKYSA8LSAwLjIKYiA8LSAwLjUKCnBhcl8wIDwtIGMoY29uLHcsYSxiKQoKc2ltMSA8LSBzaW1fZ2FyY2gocGFyXzAsbikKaF90IDwtIHNpbTEkaF90ClJfdCA8LSBzaW0xJFJfdAoKcGxvdChzaW0xJGhfdCwgdHlwZSA9ICJsIiwgeWxhYiA9IGV4cHJlc3Npb24oaFt0XSkpCmdyaWQoMTApCmBgYAoKCiMgTWFpbiBPYmplY3RpdmUKU3VwcG9zZSB3ZSB3b3VsZCBsaWtlIHRvIGZvcmVjYXN0IHRoZSB2b2xhdGlsaXR5IG92ZXIgdGhlIGxhc3QgMTAlIHBlcmlvZHMgb24gYSBkeW5hbWljIGJhc2lzLiBUbyBkbyBzbywgbGV0J3MgZmlyc3Qgc3BsaXQgdGhlIGRhdGE6CmBgYHtyfQpOX3Rlc3QgPC0gMC4xKm4KaW5kZXggPC0gMTpuCmluZGV4X3Rlc3QgPC0gKG4gLSBOX3Rlc3QgKyAxKTpuCmluZGV4X3RyYWluIDwtIGluZGV4WyFpbmRleCAlaW4lIGluZGV4X3Rlc3RdCmhfdHJhaW4gPC0gaF90W2luZGV4X3RyYWluXQpoX3Rlc3QgPC0gaF90W2luZGV4X3Rlc3RdClJfdHJhaW4gPC0gUl90W2luZGV4X3RyYWluXQpSX3Rlc3QgPC0gUl90W2luZGV4X3Rlc3RdCmBgYApUaGUgcXVlc3Rpb24gcmVtYWlucyBob3cgY2FuIHdlIGZvcmVjYXN0IHRoZSBgaF90ZXN0YCBwcm9jZXNzIGdpdmVuIHRoZSBgaF90cmFpbmA/IFRvIGFuc3dlciB0aGlzLCBJIHdpbGwgY29uc2lkZXIgdHdvIGFwcHJvYWNoZXMuIE9uZSBpcyBhIG1vZGVsIGRyaXZlbiAoR0FSQ0gpIGFuZCB0aGUgb3RoZXIgaXMgZGF0YSBkcml2ZW4gKE1MKS4KCiMjIE1vZGVsIEJhc2VkIEZvcmVjYXN0CkEgc2ltcGxlIGFwcHJvYWNoIHRvIGZvcmVjYXN0IGZ1dHVyZSB2b2xhdGlsaXR5IGlzIHRvIGJ1aWxkIHRoZSB2b2xhdGlsaXR5IHRlcm0gc3RydWN0dXJlIHVzaW5nIEdBUkNILiBUbyBkbyBzbywgd2UgbmVlZCB0byBjYWxpYnJhdGUgYSBHQVJDSCBtb2RlbCwgd2hpY2ggcmVxdWlyZXMgYW4gZXN0aW1hdGlvbiBwcm9jZWR1cmUgdXNpbmcgYWN0dWFsIGRhdGEuIFRoZSB2b2xhdGlsaXR5IHRlcm0gc3RydWN0dXJlIGlzIGNvbnN0cnVjdGVkIHRvIGV2YWx1YXRlIHRoZSBjb252ZXJnZW5jZSBvZiB0aGUgY3VycmVudCB2b2xhdGlsaXR5IHRvIHRoZSBsb25nLXRlcm0gb25lLiBJbiBvdGhlciB3b3JkcywgaXQgaGVscHMgdXMgdG8gYXNzZXNzIHdoZXRoZXIgdGhlIGN1cnJlbnQgdm9sYXRpbGl0eSBsZXZlbCBpcyBncmVhdGVyIG9yIGxlc3MgdGhhbiB0aGUgbG9uZy10ZXJtIG9uZS4gQWxzbywgaXQgYW5zd2VycyB0aGUgcXVlc3Rpb24gaG93IGxvbmcgaXQgd291bGQgdGFrZSB0aGUgY3VycmVudCB2b2xhdGlsaXR5IHRvIGNvbnZlcmdlIGJhY2sgdG8gdGhlIGxvbmctdGVybSBsZXZlbC4gCgpGb3IgYmFjay10ZXN0aW5nLCBpdCBpcyBtb3JlIHJlYWxpc3RpYyB0byBjb25zdHJ1Y3QgdGhlIGZvcmVjYXN0IG9uIGEgZHluYW1pYyBiYXNpcy4gVGhpcyBjYW4gYmUgYWNoaWV2ZWQgYnkgY29uc2lkZXJpbmcgdGhlIHJlYWxpemVkIHZvbGF0aWxpdHkgYXMgYW4gdXBkYXRlIGluIHRoZSBuZXh0IHBlcmlvZCB2b2xhdGlsaXR5ICBmb3JlY2FzdGluZyBwcm9jZWR1cmUuIFRoaXMgY2FuIGJlIGFjaGlldmVkIGFzIGZvbGxvd3MKCmBgYHtyLGZpZy5hbGlnbj0iY2VudGVyIn0KZ190cmFpbiA8LSBnYXJjaEZpdChmb3JtdWxhID0gfiBnYXJjaCgxLDEpLCBkYXRhID0gUl90cmFpbiAsdHJhY2UgPSBGKQp0aGV0YV9oYXQgPC0gIGdfdHJhaW5AZml0JGNvZWYKZ2FtbWEgPC0gdGhldGFfaGF0WzNdICsgdGhldGFfaGF0WzRdCncgPC0gdGhldGFfaGF0WzJdCmhfZm9yIDwtICBoX3RbbGVuZ3RoKGluZGV4X3RyYWluKV0KaF9maXQgPC0gbnVtZXJpYygpCmZvcihpIGluIDE6bGVuZ3RoKGluZGV4X3Rlc3QpKXsgCiAgaF9uZXh0IDwtIHcgKyBnYW1tYSpoX3RbbGVuZ3RoKGluZGV4X3RyYWluKStpLTFdCiAgaF9maXQgPC0gYyhoX2ZpdCxoX25leHQpCn0KCnsKICBwbG90KGhfdGVzdH5pbmRleF90ZXN0LCB0eXBlID0gImwiLCB5bGFiID0gZXhwcmVzc2lvbihoW3RdKSwgY29sID0gMSkKICBhYmxpbmUoaCA9IHcvKDEtZ2FtbWEpLGx0eSA9IDIpCiAgbGluZXMoaF9maXR+aW5kZXhfdGVzdCxjb2wgPSAzLGx0eSA9IDEpCiAgbGVnZW5kKCJ0b3BsZWZ0IixjKCJUcnVlIiwiR0FSQ0giKSwgY29sID0gYygxLDMpKQogIGdyaWQoMTApCn0KYGBgCgpUbyBzZWUgaG93IHRoZSB0d28gY29tcGFyZSwgd2UgY2FuIHBsb3QgdGhlIHJlYWxpemVkIHZlcnN1cyB0aGUgZm9yZWNhc3QuCmBgYHtyLGZpZy5hbGlnbj0iY2VudGVyIn0KewogIHBsb3QoaF90ZXN0fmhfZml0LHBjaCA9IDIwLGNleCA9IDAuNSkKICBhYmxpbmUoYT0wLGI9MSxsdHkgID0gMikKICBncmlkKDEwKQp9CmBgYAoKSW4gdGVybXMgb2YgcmVncmVzc2lvbiwgd2UgaGF2ZQpgYGB7ciByZXN1bHRzID0gImFzaXMiLG1lc3NhZ2U9RkFMU0Usd2FybmluZz1GQUxTRX0gCmxtX2dhcmNoIDwtIGxtKGhfdGVzdH5oX2ZpdCkKc3RhcmdhemVyOjpzdGFyZ2F6ZXIobG1fZ2FyY2gsdHlwZSA9ICJodG1sIixjb3ZhcmlhdGUubGFiZWxzID0gIkdBUkNIIEZvcmVjYXN0IixkZXAudmFyLmxhYmVscyA9ICJBY3R1YWwgVm9sYXRpbGl0eSIpCmBgYAoKV2Ugb2JzZXJ2ZSB0aGF0IHRoZSBHQVJDSCBmb3JlY2FzdHMgY2xvc2VseSBtaW1pYyB0aGUgYWN0dWFsIHRlc3QgZGF0YSwgaS5lLiBpbnRlcmNlcHQgb2YgemVybywgc2xvcGUgcm91Z2hseSAxLCBhbmQgJFJeMiQgb2YgNTZcJS4gTm9uZXRoZWxlc3MsIHRoZSBtYWluIGlzc3VlIGhlcmUgaXMgdGhhdCB0aGUgZGF0YSBzZXQsIGJ5IGNvbnN0cnVjdGlvbiwgZm9sbG93cyBhIEdBUkNIIG1vZGVsLiBIZW5jZSwgdGhpcyBzaG91bGQgbm90IGNvbWUgYXMgYSBzdXJwcmlzZS4gVGhlIG1vcmUgY2hhbGxlbmdpbmcgYW5kIHJlYWxpc3RpYyB0YXNrIGlzIHdoZXRoZXIgdGhlIGRhdGEgaW4gcmVhbC1saWZlIGlzIHN0YXRpb25hcnkgYW5kIG9iZXlzIHRvIHN1Y2ggZGF0YSBnZW5lcmF0aW5nIGZ1bmN0aW9uLiAKCgojIyBNYWNoaW5lIExlYXJuaW5nIEFwcHJvYWNoCkEgZGF0YS1kcml2ZW4gYXBwcm9hY2ggZG9lcyBub3QgcG9zc2VzcyBpbmZvcm1hdGlvbiBhYm91dCB0aGUgdW5kZXJseWluZyBwcm9jZXNzIG9mIHRoZSBkYXRhLiBIZW5jZSwgYW4gTUwgYXBwcm9hY2ggd291bGQgbGVhcm4gdGhlIHZvbGF0aWxpdHkgYmVoYXZpb3IgZnJvbSB0aGUgZGF0YSB3aXRob3V0IGltcG9zaW5nIGEgc3RydWN0dXJhbCBtb2RlbC4gVGhlIHRhc2sgaGVyZSBpcyB0byBpbnZlc3RpZ2F0ZSB0aGUgY2FwYWJpbGl0eSBvZiB0aGUgTUwgdG8gbWFwIHRoZSBkYXRhIGludG8gYSB2b2xhdGlsaXR5IGZvcmVjYXN0IGFuZCBjb21wYXJlIGl0IHdpdGggcmVzcGVjdCB0byBpdHMgYmVuY2htYXJrLCB0aGUgIEdBUkNIIG1vZGVsLiAgCgpJIHVzZSBhIHNpbWlsYXIgY29kZSBmcm9tIG15IHByZXZpb3VzIFtub3Rlc10oaHR0cHM6Ly9ycHVicy5jb20vc2ltYWFuODQvTUxfaG9yc2UpIGZvciBpbXBsZW1lbnRhdGlvbiBvZiBNTCBhbGdvcml0aG1zLiBJbiB0aGlzIGNhc2UsIEkgZm9jdXMgb24gdGhlIHN1cHBvcnQgdmVjdG9yIG1hY2hpbmVzIChTVk0pIHdpdGggYSByYWRpYWwga2VybmVsLiBUbyBkbyBzbywgbXkgZmVhdHVyZSBzcGFjZSBpcyBjb25zdHJ1Y3RlZCB1c2luZyB0aGUgY3VycmVudCBwcm9jZXNzIGl0c2VsZiAoc3F1YXJlZCkgYW5kIHRoZSBjdXJyZW50IGxvZy1yZWFsaXplZCB2b2xhdGlsaXR5LiBUaGUgb3V0Y29tZSB2YXJpYWJsZSwgb24gdGhlIG90aGVyIGhhbmQsIGlzIGdpdmVuIGJ5IHRoZSBuZXh0IHBlcmlvZCBsb2ctcmVhbGl6ZWQgdm9sYXRpbGl0eS4gTm90ZSB0aGF0IHdlIHVzZSBhIGxvZyB0cmFuc2Zvcm1hdGlvbiwgZm9sbG93ZWQgYnkgYW4gZXhwb25lbnRpYWwgb25lLCB0byBtYWtlIHN1cmUgdGhlIGZpdHRlZCB2b2xhdGlsaXRpZXMgYXJlIG5vbi1uZWdhdGl2ZS4KCgpTaW1pbGFyIHRvIHRoZSBwcmV2aW91cyBhbmFseXNpcywgd2Ugc3BsaXQgdGhlIGRhdGEgaW50byBpbi1zYW1wbGUgYW5kIG91dC1vZi1zYW1wbGUgc2V0cy4gQWRkaXRpb25hbGx5LCBmb3IgYSBtb3JlIGVmZmljaWVudCBpbXBsZW1lbnRhdGlvbiwgSSByZWZlciB0byB0aGUgYGRvUGFyYWxsZWxgIHBhY2thZ2UgZm9yIHBhcmFsbGVsIGNvbXB1dGluZyBvbiBhbiA4LWNvcmUgbGFwdG9wIGFzIGRlbW9uc3RyYXRlZCBiZWxvdzoKYGBge3IsbWVzc2FnZT1GQUxTRSx3YXJuaW5nPUZBTFNFLGZpZy5hbGlnbj0iY2VudGVyIn0KbGlicmFyeShjYXJldCkKbGlicmFyeShkb1BhcmFsbGVsKQoKZHMgPC0gbmEub21pdChkYXRhLmZyYW1lKGggPSBsb2coaF90KSwgciA9IFJfdF4yLCB5ID0gbG9nKGMoaF90Wy0xXSxOQSkpICkpCmluZGV4IDwtIDE6bnJvdyhkcykKaW5kZXhfdHJhaW4yIDwtIGluZGV4X3RyYWluCmluZGV4X3RyYWluMiA8LSBpbmRleF90cmFpbjJbLWxlbmd0aChpbmRleF90cmFpbjIpXSAjIGF2b2lkIHRoZSBsb29rIGFoZWFkIG9ic2VydmF0aW9uCmluZGV4X3Rlc3QgPC0gaW5kZXhbIWluZGV4ICVpbiUgaW5kZXhfdHJhaW4yXQoKZHNfdHJhaW4gPC0gZHNbaW5kZXhfdHJhaW4sXQpkc190ZXN0IDwtIGRzW2luZGV4X3Rlc3QsXQoKbW9kZWxfaSA8LSAic3ZtUmFkaWFsIgp0cmN0cmwgPC0gdHJhaW5Db250cm9sKG1ldGhvZCA9ICJyZXBlYXRlZGN2IiwgbnVtYmVyID0gMTAsIHJlcGVhdHMgPSA1KQoKY2wgPC0gbWFrZVBTT0NLY2x1c3Rlcig4KQpyZWdpc3RlckRvUGFyYWxsZWwoY2wpCgp0cmFpbl9tb2RlbCA8LSB0cmFpbih5fiBoICsgciwgZGF0YSA9IGRzX3RyYWluLCBtZXRob2QgPSBtb2RlbF9pLAogICAgICAgICAgICAgICAgICAgICB0ckNvbnRyb2w9dHJjdHJsLAogICAgICAgICAgICAgICAgICAgICBwcmVQcm9jZXNzID0gYygiY2VudGVyIiwgInNjYWxlIiksCiAgICAgICAgICAgICAgICAgICAgIHR1bmVMZW5ndGggPSAxMCkKCnN0b3BDbHVzdGVyKGNsKQpgYGAKCkdpdmVuIHRoZSB0cmFpbmVkIG1vZGVsLCB3ZSBmb3JlY2FzdCB0aGUgbmV4dCBwZXJpb2QgdXNpbmcgdGhlIHRlc3QgZGF0YXNldCBhbmQgdXNlIHRoZSBleHBvbmVudGlhbCB0cmFuc2Zvcm1hdGlvbiB0byByZXRyaWV2ZSB0aGUgdm9sYXRpbGl0eSBsZXZlbC4gQXMgYSBzdW1tYXJ5LCB3ZSBwbG90IGFsdG9nZXRoZXI6CmBgYHtyLGZpZy5hbGlnbj0iY2VudGVyIn0KaF9tbCA8LSBleHAocHJlZGljdCh0cmFpbl9tb2RlbCxkc190ZXN0KSkKewogIHBsb3QoaF90ZXN0fmluZGV4X3Rlc3QsdHlwZSA9ICJsIikKICBsaW5lcyhoX21sfmluZGV4X3Rlc3QsY29sID0gMikKICBsaW5lcyhoX2ZpdH5pbmRleF90ZXN0LGNvbCA9IDMpCiAgbGVnZW5kKCJ0b3BsZWZ0IixjKCJUcnVlIiwiTUwiLCJHQVJDSCIpKQogIGdyaWQoMTApCn0KYGBgCkZyb20gdGhlIGFib3ZlIGdyYXBoLCB3ZSBvYnNlcnZlIHRoYXQgdGhlIE1MIGFsZ29yaXRobSBjbG9zZWx5IG1pbWljcyB0aGUgR0FSQ0ggbW9kZWwgaW4gdGVybXMgb2YgZm9yZWNhc3RzLiBTaW5jZSB0aGUgZGF0YSBpbiBvdXIgY2FzZSBpcyBzdGF0aW9uYXJ5LCBsZWFybmluZyBmcm9tIGhpc3RvcmljYWwgZGF0YSBzaG91bGQgYmUgcmVsZXZhbnQgZm9yIGZ1dHVyZSBhcHBsaWNhdGlvbnMuIEF0IHRoZSBzYW1lIHRpbWUsIHdlIGRpc2Nlcm4gdGhhdCB0aGUgTUwgYWxnb3JpdGhtIGlzIGFibGUgdG8gdW5jb3ZlciB0aGUgYXV0by1yZWdyZXNzaXZlIGNvbXBvbmVudCBpbiB0aGUgdm9sYXRpbGl0eSBwcm9jZXNzLiAKCkluIHRlcm1zIG9mIHJlZ3Jlc3Npb24sICB3ZSBub3RlIHRoZSBNTCBmb3JlY2FzdCBjbG9zZWx5IGNvcnJlc3BvbmRzIHRvIHRoZSBHQVJDSCBzdGF0aXN0aWNzLCB3aXRoIGEgc2xpZ2h0bHkgc21hbGxlciAkUl4yJC4gVGhpcyBjYW4gYmUgaW5mZXJyZWQgZnJvbSB0aGUgZm9sbG93aW5nIHJlZ3Jlc3Npb246CmBgYHtyIHJlc3VsdHMgPSAiYXNpcyIsbWVzc2FnZT1GQUxTRSx3YXJuaW5nPUZBTFNFfSAKbG1fbWwgPC0gbG0oaF90ZXN0fmhfbWwpCnN0YXJnYXplcjo6c3RhcmdhemVyKGxpc3QobG1fbWwsbG1fZ2FyY2gpLHR5cGUgPSAiaHRtbCIsY292YXJpYXRlLmxhYmVscyA9IGMoIk1MIEZvcmVjYXN0IiwiR0FSQ0ggRm9yZWNhc3QiKSxkZXAudmFyLmxhYmVscyA9ICJBY3R1YWwgVm9sYXRpbGl0eSIpCmBgYAoKCiMgQ29uY2x1ZGluZyBSZW1hcmtzClRoZSBhYm92ZSBleHBlcmltZW50IGlzIGNvbmR1Y3RlZCB1c2luZyBzaW11bGF0ZWQgZGF0YSBmcm9tIGEgR0FSQ0ggbW9kZWwuIEZvciB0aGlzLCBpdCBpcyBub3Qgc3VycHJpc2luZyB0aGF0IHRoZSBHQVJDSCBtb2RlbCBwcm92aWRlcyBhIGJldHRlciBhcHByb3hpbWF0aW9uIG9mIGZ1dHVyZSByZWFsaXplZCB2b2xhdGlsaXR5LiBIb3dldmVyLCB0d28gbWFqb3IgaXNzdWVzIHNob3VsZCBiZSBjb25zaWRlcmVkIGFzIGEgcHJpbWFyeSBpbnZlc3RpZ2F0aW9uLiBGaXJzdCwgaG93IGRvZXMgdGhlIHJlc3VsdCBjaGFuZ2UgaWYgdGhlcmUgaXMgYSBzdHJ1Y3R1cmFsIGJyZWFrIGluIHRoZSBkYXRhPyBGb3IgaW5zdGFuY2UsIGlmIHRoZSBkYXRhIGlzIG5vbi1zdGF0aW9uYXJ5LCBob3cgZG9lcyBlYWNoIG1vZGVsIGNvbXBhcmU/ICBTZWNvbmQsIHdoYXQgaXMgdGhlIGVjb25vbWljIHZhbHVlIG9mIGRlcGxveWluZyBNTCB2ZXJzdXMgR0FSQ0ggbW9kZWxzPyAKCkZvciB0aGUgZmlyc3QgaXNzdWUsIHdlIG1heSBhZGQgc2hvY2tzIHRvIHRoZSB0cmFpbmluZyBzYW1wbGUgYW5kIHRlc3QgdGhlIHNlbnNpdGl2aXR5IGluIHRoZSB0ZXN0IHNldC4gSW4gb3RoZXIgd29yZHMsIHdoaWNoIGFwcHJvYWNoIGlzIG1vcmUgcm9idXN0PyBXZSBzaG91bGQgZ2V0IGEgYmV0dGVyIHBpY3R1cmUgdXNpbmcgcmVhbCBkYXRhLiBGb3IgdGhlIHNlY29uZCBpc3N1ZSwgYSBwb3J0Zm9saW8gc2VsZWN0aW9uIHByb2JsZW0gaXMgb2YgaGlnaCByZWxldmFuY2UuIFdlIGNhbiB0ZXN0IHRoZSBlY29ub21pYyBwZXJmb3JtYW5jZSBvZiBnbG9iYWwgbWluaW11bSBwb3J0Zm9saW8gY29uc3RydWN0ZWQgdXNpbmcgZWl0aGVyIG1vZGVsLiBJIGxlYXZlIGJvdGggZm9yIGZ1dHVyZSByZXNlYXJjaC4KCgoK