Packages & parameters

We start by loading the required packages & functions.

rm(list = ls())
library(tidyverse)
library(Rcpp)
library(mvtnorm)
library(reshape2)
library(latex2exp)
library(cowplot)
sourceCpp("c_functions.cpp")
source("R_functions.R")

Then, we set some default parameter values. Note: because T is reserved in R, we use TT instead.

k   <- 12
TT  <- 36          # TT = number of observations
a_x <- 0           # Constant in the AR process
a_y <- 0           # Constant in the AR process
r_x <- 0.9         # rho_x
r_y <- 0.7         # rho_y
s_x <- 0.2         # sigma_x
s_y <- 0.4         # sigma_y
cor <- 0           # Correlation between innovations e_y and e_x
n_sim <- 10^5      # Number of simulations 
n_points <- 10^3   # Number of discretization points in the approximation of the integral
STOP <- 10^3       # Point where the discretization stops

Testing the functions

All functions were coded both in R and C. The C versions were only faster for the autoregressive simulations. However, the matrix multiplications in the large dimensional simulations made the C (or C++ in Armadillo) code slower. Below, we test the following:

In the first function, processes are allowed to have a drift, but this drift does not impact the MSE, so we set it to zero.

We use tictoc to compute running times.

tictoc::tic()
quad_err_C_2d2(a_x, a_y, r_x, r_y, s_x, s_y, cor, TT, k, n_sim)
[1] 0.4236085 0.3981790
tictoc::toc()
2.775 sec elapsed
tictoc::tic()
MSE_simulation(a_x, a_y, r_x, r_y, s_x, s_y, cor, TT, k, n_sim)
[1] 0.4211911 0.3793136
tictoc::toc()
15.314 sec elapsed
tictoc::tic()
MSE_integral_ind(r_x, r_y, s_y, TT, k, n_points, STOP)
[1] 0.4217557
tictoc::toc()
0.197 sec elapsed
tictoc::tic()
MSE_integral(r_x, r_y, s_y, cor, TT, k, n_points, STOP)
[1] 0.4217697
tictoc::toc()
5.622 sec elapsed

Even with 100,000 simulations, the values are already close.

First study

The first step is to evaluate the accuracy of the methods and quantify how long they take.
The C functions were not designed to take vectors parameter values, thus we resort to ugly loops, though functional programming would be more elegant (see the sensitivity analysis below). One important reason why a loop is convenient is that it’s easy to reset the (pseudo-)random number generator.
This is not costly computationally because the number of iterations is small.
Nevertheless, the loop below is quite time-consuming (a few hours on a 2019 MacBook Pro).

n <- 1000*2^(0:12)
stop <- 100 * round(log(n))
val_AR_sim <- 0
val_S_sim <- 0
val_integral_ind <- 0
var_AR_sim <- 0
var_S_sim <- 0
cpu_AR_sim <- 0
cpu_S_sim <- 0
cpu_integral_ind <- 0
for(i in 1:length(n)){
    set.seed(42)
    tictoc::tic()
    tmp <- quad_err_C_2d2(a_x, a_y, r_x, r_y, s_x, s_y, cor, TT, k, n[i])
    val_AR_sim[i] <- tmp[1]
    var_AR_sim[i] <- tmp[2]
    z <- tictoc::toc(quiet = T)
    cpu_AR_sim[i] <- z$toc - z$tic
    
    set.seed(42)
    tictoc::tic()
    tmp <- MSE_simulation(a_x, a_y, r_x, r_y, s_x, s_y, cor, TT, k, n[i])
    val_S_sim[i] <- tmp[1]
    var_S_sim[i] <- tmp[2]
    z <- tictoc::toc(quiet = T)
    cpu_S_sim[i] <- z$toc - z$tic
    
    set.seed(42)
    tictoc::tic()
    val_integral_ind[i] <- MSE_integral_ind(r_x, r_y, s_y, TT, k, n[i], STOP = stop[i])
    z <- tictoc::toc(quiet = T)
    cpu_integral_ind[i] <- z$toc - z$tic
}

val <- data.frame(n, val_AR_sim, val_S_sim, val_integral_ind) %>%
    pivot_longer(-n, names_to = "type", values_to = "value") %>%
    mutate(indicator = "MSE",
           type = substr(type, 5, 9))
save(val, file="values.RData")

cpu <- data.frame(n, cpu_AR_sim, cpu_S_sim, cpu_integral_ind) %>%
    pivot_longer(-n, names_to = "type", values_to = "value") %>%
    mutate(indicator = "CPU")
save(cpu, file = "cpu.RData")

CI <- data.frame(n, var_AR_sim, var_S_sim) %>%
    pivot_longer(-n, names_to = "type", values_to = "value") %>%
    mutate(indicator = "CI",
           type = substr(type, 5, 9),
           value = 1.96 * sqrt(value/n)) # Width of confidence interval
save(var, file = "CI.RData")

Then, we wrangle the results and plot them.
We start with the raw MSE values.

data_g <- bind_rows(val, CI) %>%
    pivot_wider(names_from = "indicator", values_from = "value") %>%
    mutate(lower = MSE - CI, upper = MSE + CI) %>%
    pivot_longer(3:6, names_to = "indicator", values_to = "value")
plot_MSE_n <- data_g %>%
    ggplot(aes(x = n/1000, y = value, color = type)) + 
    geom_line(data = data_g %>% filter(indicator == "MSE")) + 
    geom_line(data = data_g %>% filter(indicator == "lower"), linetype = 2) +
    geom_line(data = data_g %>% filter(indicator == "upper"), linetype = 3) +
    xlab(TeX("\\textbf{Points (in thousands)}")) + ylab(TeX("\\textbf{Mean Squared Error}")) +
    #scale_color_viridis(option="inferno", begin = 0.4, end = 0.9, discrete = TRUE)
    scale_color_manual(name = "Type", 
                       labels = c("Process", "Integral", "Multivariate"), 
                       values = c("#0B56B0", "#FD6909", "#12A74C")) +
    scale_x_log10() +
    theme(aspect.ratio = 1.2, legend.position = c(0.8, 0.2))
plot_MSE_n

We see that Monte-Carlo simulations, as expected, yield values that oscillate a lot. The speed of convergence is proportional to \(n^{-1/2}\), so in our case, the error has an order of magnitude of \(10^{-3}\), which is what we observe.

Next, we turn to computation times.

plot_CPU_n <- cpu %>%
    ggplot(aes(x = n/1000, y = value, color = type)) + geom_line() +
    xlab(TeX("\\textbf{Points (in thousands)}")) + ylab(TeX("\\textbf{CPU time (seconds) - logscale}")) +
    scale_y_log10() +
    scale_color_manual(name = "Type", 
                       labels = c("Process", "Integral", "Multivariate"), 
                       values = c("#0B56B0", "#FD6909", "#12A74C")) +
    theme(aspect.ratio = 1.2, legend.position = c(0.8, 0.2))
plot_CPU_n

There is almost a 10-fold different between each color!

Below, in order to save time, we discard the slowest method.

Save both plots together

plot_grid(plot_MSE_n, plot_CPU_n, ncol = 2, nrow = 1)
Removed 13 row(s) containing missing values (geom_path).Removed 13 row(s) containing missing values (geom_path).

#ggsave(paste(folder, "comparison.pdf", sep='/'))

Impact of T

Next, we switch to other values of T. It’s interesting to see what happens when the sample size augments.
Again, we resort to a loop.

TTT <- seq(5,100,5)
value_AR_sim <- 0
value_integral_ind <- 0
cpu_AR_sim <- 0
cpu_integral_ind <- 0
for(i in 1:length(TTT)){
    tictoc::tic()
    value_AR_sim[i] <- quad_err_C_2d2(a_x, a_y, r_x, r_y, s_x, s_y, cor, TTT[i], k, n_sim)[1]
    z <- tictoc::toc(quiet = T)
    cpu_AR_sim[i] <- z$toc - z$tic

    tictoc::tic()
    value_integral_ind[i] <- MSE_integral_ind(r_x, r_y, s_y, TTT[i], k, n_points, 100 * round(log(n_points)) )
    z <- tictoc::toc(quiet = T)
    cpu_integral_ind[i] <- z$toc - z$tic
}

values_TT <- data.frame(TTT, value_AR_sim, value_integral_ind) %>%
    pivot_longer(-TTT, names_to = "type", values_to = "value")
save(values_TT, file = "values_TT.RData")

cpu_TT <- data.frame(TTT, cpu_AR_sim, cpu_integral_ind) %>%
    pivot_longer(-TTT, names_to = "type", values_to = "value")
save(cpu_TT, file = "cpu_TT.RData")

Then we analyze the results.

plot_MSE_T <- values_TT %>%
    ggplot(aes(x = TTT, y = value, color = type)) + geom_line() + 
    xlab(TeX("\\textbf{T}")) + ylab(TeX("\\textbf{Mean Squared Error}")) +
    #scale_color_viridis(option="inferno", begin = 0.4, end = 0.9, discrete = TRUE)
    scale_color_manual(name = "Type", 
                       labels = c("Process", "Integral"), 
                       values = c("#0B56B0", "#FD6909")) +
    theme(aspect.ratio = 1.2, legend.position = c(0.8, 0.6))
plot_MSE_T

When T increases, the MSE follows. The range of \(y\) values makes it hard to discern the difference between the two curves.

plot_CPU_T <- cpu_TT %>%
    ggplot(aes(x = TTT, y = value, color = type)) + geom_line() +
    xlab(TeX("\\textbf{T}")) + ylab(TeX("\\textbf{CPU time (seconds) - logscale}")) +
    scale_y_log10() +
    scale_color_manual(name = "Type", 
                       labels = c("Process", "Integral"), 
                       values = c("#0B56B0", "#FD6909")) +
    theme(aspect.ratio = 1.2, legend.position = c(0.8, 0.6))
plot_CPU_T

Overall, the discretized integral is more than 100 times faster that the MC simulations.

ggsave("comparison_09.pdf")
Saving 7 x 7 in image
LS0tCnRpdGxlOiAiUHJlZGljdGl2ZSByZWdyZXNzaW9uczogY29tcGFyaW5nIG1ldGhvZHMiCm91dHB1dDogaHRtbF9ub3RlYm9vawotLS0KCiMgUGFja2FnZXMgJiBwYXJhbWV0ZXJzCgpXZSBzdGFydCBieSBsb2FkaW5nIHRoZSByZXF1aXJlZCBwYWNrYWdlcyAmIGZ1bmN0aW9ucy4KCmBgYHtyLCBtZXNzYWdlID0gRiwgd2FybmluZyA9IEYsIHNpemUgPSAiZm9vdG5vdGVzaXplIn0Kcm0obGlzdCA9IGxzKCkpCmxpYnJhcnkodGlkeXZlcnNlKQpsaWJyYXJ5KFJjcHApCmxpYnJhcnkobXZ0bm9ybSkKbGlicmFyeShyZXNoYXBlMikKbGlicmFyeShsYXRleDJleHApCmxpYnJhcnkoY293cGxvdCkKc291cmNlQ3BwKCJjX2Z1bmN0aW9ucy5jcHAiKQpzb3VyY2UoIlJfZnVuY3Rpb25zLlIiKQpgYGAKClRoZW4sIHdlIHNldCBzb21lICoqZGVmYXVsdCBwYXJhbWV0ZXIqKiB2YWx1ZXMuICoqTm90ZSoqOiBiZWNhdXNlIFQgaXMgcmVzZXJ2ZWQgaW4gUiwgd2UgdXNlIFRUIGluc3RlYWQuCgpgYGB7ciwgc2l6ZSA9ICJmb290bm90ZXNpemUifQprICAgPC0gMTIKVFQgIDwtIDM2ICAgICAgICAgICMgVFQgPSBudW1iZXIgb2Ygb2JzZXJ2YXRpb25zCmFfeCA8LSAwICAgICAgICAgICAjIENvbnN0YW50IGluIHRoZSBBUiBwcm9jZXNzCmFfeSA8LSAwICAgICAgICAgICAjIENvbnN0YW50IGluIHRoZSBBUiBwcm9jZXNzCnJfeCA8LSAwLjkgICAgICAgICAjIHJob194CnJfeSA8LSAwLjcgICAgICAgICAjIHJob195CnNfeCA8LSAwLjIgICAgICAgICAjIHNpZ21hX3gKc195IDwtIDAuNCAgICAgICAgICMgc2lnbWFfeQpjb3IgPC0gMCAgICAgICAgICAgIyBDb3JyZWxhdGlvbiBiZXR3ZWVuIGlubm92YXRpb25zIGVfeSBhbmQgZV94Cm5fc2ltIDwtIDEwXjUgICAgICAjIE51bWJlciBvZiBzaW11bGF0aW9ucyAKbl9wb2ludHMgPC0gMTBeMyAgICMgTnVtYmVyIG9mIGRpc2NyZXRpemF0aW9uIHBvaW50cyBpbiB0aGUgYXBwcm94aW1hdGlvbiBvZiB0aGUgaW50ZWdyYWwKU1RPUCA8LSAxMF4zICAgICAgICMgUG9pbnQgd2hlcmUgdGhlIGRpc2NyZXRpemF0aW9uIHN0b3BzCmBgYAoKCiMgVGVzdGluZyB0aGUgZnVuY3Rpb25zCgpBbGwgZnVuY3Rpb25zIHdlcmUgY29kZWQgYm90aCBpbiAqKlIqKiBhbmQgKipDKiouIFRoZSBDIHZlcnNpb25zIHdlcmUgb25seSBmYXN0ZXIgZm9yIHRoZSBhdXRvcmVncmVzc2l2ZSBzaW11bGF0aW9ucy4KSG93ZXZlciwgdGhlIG1hdHJpeCBtdWx0aXBsaWNhdGlvbnMgaW4gdGhlIGxhcmdlIGRpbWVuc2lvbmFsIHNpbXVsYXRpb25zIG1hZGUgdGhlIEMgKG9yIEMrKyBpbiBBcm1hZGlsbG8pIGNvZGUgc2xvd2VyLgpCZWxvdywgd2UgdGVzdCB0aGUgZm9sbG93aW5nOgoKLSAqKlByb2Nlc3MqKjogcmVseWluZyBvbiB0aGUgZnVuY3Rpb24gdGhhdCBzaW11bGF0ZXMgY29ycmVsYXRlZCBiaXZhcmlhdGUgQVIoMSkgcHJvY2Vzc2VzIChpbiBDKTogKipxdWFkX2Vycl9DXzJkKiogICAgCi0gKipNdWx0aXZhcmlhdGUqKjogcmVseWluZyBvbiB0aGUgZnVuY3Rpb24gdGhhdCBzaW11bGF0ZXMgdmVjdG9yIHogdXNpbmcgdGhlIGxhcmdlIGNvdmFyaWFuY2UgbWF0cml4IChpbiBSKTogKipNU0Vfc2ltdWxhdGlvbioqICAgIAotICoqSW50ZWdyYWwqKjogcmVseWluZyBvbiB0aGUgZnVuY3Rpb24gdGhhdCB1c2VzIG51bWVyaWNhbCBpbnRlZ3JhdGlvbiB0byBjb21wdXRlIHRoZSBNU0U6ICoqTVNFX2ludGVncmFsKiogICAKCkluIHRoZSBmaXJzdCBmdW5jdGlvbiwgcHJvY2Vzc2VzIGFyZSBhbGxvd2VkIHRvIGhhdmUgYSBkcmlmdCwgYnV0IHRoaXMgZHJpZnQgZG9lcyBub3QgaW1wYWN0IHRoZSBNU0UsIHNvIHdlIHNldCBpdCB0byB6ZXJvLiAKCldlIHVzZSB0aWN0b2MgdG8gY29tcHV0ZSBydW5uaW5nIHRpbWVzLgoKYGBge3IsIHNpemUgPSAiZm9vdG5vdGVzaXplIn0KdGljdG9jOjp0aWMoKQpxdWFkX2Vycl9DXzJkMihhX3gsIGFfeSwgcl94LCByX3ksIHNfeCwgc195LCBjb3IsIFRULCBrLCBuX3NpbSkKdGljdG9jOjp0b2MoKQoKdGljdG9jOjp0aWMoKQpNU0Vfc2ltdWxhdGlvbihhX3gsIGFfeSwgcl94LCByX3ksIHNfeCwgc195LCBjb3IsIFRULCBrLCBuX3NpbSkKdGljdG9jOjp0b2MoKQoKdGljdG9jOjp0aWMoKQpNU0VfaW50ZWdyYWxfaW5kKHJfeCwgcl95LCBzX3ksIFRULCBrLCBuX3BvaW50cywgU1RPUCkKdGljdG9jOjp0b2MoKQoKdGljdG9jOjp0aWMoKQpNU0VfaW50ZWdyYWwocl94LCByX3ksIHNfeSwgY29yLCBUVCwgaywgbl9wb2ludHMsIFNUT1ApCnRpY3RvYzo6dG9jKCkKYGBgCgpFdmVuIHdpdGggMTAwLDAwMCBzaW11bGF0aW9ucywgdGhlIHZhbHVlcyBhcmUgYWxyZWFkeSBjbG9zZS4KCiMgRmlyc3Qgc3R1ZHkKClRoZSBmaXJzdCBzdGVwIGlzIHRvIGV2YWx1YXRlIHRoZSAqKmFjY3VyYWN5Kiogb2YgdGhlIG1ldGhvZHMgYW5kIHF1YW50aWZ5IGhvdyBsb25nIHRoZXkgdGFrZS4gICAKVGhlIEMgZnVuY3Rpb25zIHdlcmUgbm90IGRlc2lnbmVkIHRvIHRha2UgdmVjdG9ycyBwYXJhbWV0ZXIgdmFsdWVzLCB0aHVzIHdlIHJlc29ydCB0byB1Z2x5IGxvb3BzLCB0aG91Z2ggZnVuY3Rpb25hbCBwcm9ncmFtbWluZyB3b3VsZCBiZSBtb3JlIGVsZWdhbnQgKHNlZSB0aGUgc2Vuc2l0aXZpdHkgYW5hbHlzaXMgYmVsb3cpLiBPbmUgaW1wb3J0YW50IHJlYXNvbiB3aHkgYSBsb29wIGlzIGNvbnZlbmllbnQgaXMgdGhhdCBpdCdzIGVhc3kgdG8gcmVzZXQgdGhlIChwc2V1ZG8tKXJhbmRvbSBudW1iZXIgZ2VuZXJhdG9yLiAgIApUaGlzIGlzIG5vdCBjb3N0bHkgY29tcHV0YXRpb25hbGx5IGJlY2F1c2UgdGhlIG51bWJlciBvZiBpdGVyYXRpb25zIGlzIHNtYWxsLiAgIApOZXZlcnRoZWxlc3MsIHRoZSBsb29wIGJlbG93IGlzIHF1aXRlICoqdGltZS1jb25zdW1pbmcqKiAoYSBmZXcgaG91cnMgb24gYSAyMDE5IE1hY0Jvb2sgUHJvKS4KCmBgYHtyLCBtZXNzYWdlID0gRiwgd2FybmluZyA9IEYsIHNpemUgPSAiZm9vdG5vdGVzaXplIn0KbiA8LSAxMDAwKjJeKDA6MTIpCnN0b3AgPC0gMTAwICogcm91bmQobG9nKG4pKQp2YWxfQVJfc2ltIDwtIDAKdmFsX1Nfc2ltIDwtIDAKdmFsX2ludGVncmFsX2luZCA8LSAwCnZhcl9BUl9zaW0gPC0gMAp2YXJfU19zaW0gPC0gMApjcHVfQVJfc2ltIDwtIDAKY3B1X1Nfc2ltIDwtIDAKY3B1X2ludGVncmFsX2luZCA8LSAwCmZvcihpIGluIDE6bGVuZ3RoKG4pKXsKICAgIHNldC5zZWVkKDQyKQogICAgdGljdG9jOjp0aWMoKQogICAgdG1wIDwtIHF1YWRfZXJyX0NfMmQyKGFfeCwgYV95LCByX3gsIHJfeSwgc194LCBzX3ksIGNvciwgVFQsIGssIG5baV0pCiAgICB2YWxfQVJfc2ltW2ldIDwtIHRtcFsxXQogICAgdmFyX0FSX3NpbVtpXSA8LSB0bXBbMl0KICAgIHogPC0gdGljdG9jOjp0b2MocXVpZXQgPSBUKQogICAgY3B1X0FSX3NpbVtpXSA8LSB6JHRvYyAtIHokdGljCiAgICAKICAgIHNldC5zZWVkKDQyKQogICAgdGljdG9jOjp0aWMoKQogICAgdG1wIDwtIE1TRV9zaW11bGF0aW9uKGFfeCwgYV95LCByX3gsIHJfeSwgc194LCBzX3ksIGNvciwgVFQsIGssIG5baV0pCiAgICB2YWxfU19zaW1baV0gPC0gdG1wWzFdCiAgICB2YXJfU19zaW1baV0gPC0gdG1wWzJdCiAgICB6IDwtIHRpY3RvYzo6dG9jKHF1aWV0ID0gVCkKICAgIGNwdV9TX3NpbVtpXSA8LSB6JHRvYyAtIHokdGljCiAgICAKICAgIHNldC5zZWVkKDQyKQogICAgdGljdG9jOjp0aWMoKQogICAgdmFsX2ludGVncmFsX2luZFtpXSA8LSBNU0VfaW50ZWdyYWxfaW5kKHJfeCwgcl95LCBzX3ksIFRULCBrLCBuW2ldLCBTVE9QID0gc3RvcFtpXSkKICAgIHogPC0gdGljdG9jOjp0b2MocXVpZXQgPSBUKQogICAgY3B1X2ludGVncmFsX2luZFtpXSA8LSB6JHRvYyAtIHokdGljCn0KCnZhbCA8LSBkYXRhLmZyYW1lKG4sIHZhbF9BUl9zaW0sIHZhbF9TX3NpbSwgdmFsX2ludGVncmFsX2luZCkgJT4lCiAgICBwaXZvdF9sb25nZXIoLW4sIG5hbWVzX3RvID0gInR5cGUiLCB2YWx1ZXNfdG8gPSAidmFsdWUiKSAlPiUKICAgIG11dGF0ZShpbmRpY2F0b3IgPSAiTVNFIiwKICAgICAgICAgICB0eXBlID0gc3Vic3RyKHR5cGUsIDUsIDkpKQpzYXZlKHZhbCwgZmlsZT0idmFsdWVzLlJEYXRhIikKCmNwdSA8LSBkYXRhLmZyYW1lKG4sIGNwdV9BUl9zaW0sIGNwdV9TX3NpbSwgY3B1X2ludGVncmFsX2luZCkgJT4lCiAgICBwaXZvdF9sb25nZXIoLW4sIG5hbWVzX3RvID0gInR5cGUiLCB2YWx1ZXNfdG8gPSAidmFsdWUiKSAlPiUKICAgIG11dGF0ZShpbmRpY2F0b3IgPSAiQ1BVIikKc2F2ZShjcHUsIGZpbGUgPSAiY3B1LlJEYXRhIikKCkNJIDwtIGRhdGEuZnJhbWUobiwgdmFyX0FSX3NpbSwgdmFyX1Nfc2ltKSAlPiUKICAgIHBpdm90X2xvbmdlcigtbiwgbmFtZXNfdG8gPSAidHlwZSIsIHZhbHVlc190byA9ICJ2YWx1ZSIpICU+JQogICAgbXV0YXRlKGluZGljYXRvciA9ICJDSSIsCiAgICAgICAgICAgdHlwZSA9IHN1YnN0cih0eXBlLCA1LCA5KSwKICAgICAgICAgICB2YWx1ZSA9IDEuOTYgKiBzcXJ0KHZhbHVlL24pKSAjIFdpZHRoIG9mIGNvbmZpZGVuY2UgaW50ZXJ2YWwKc2F2ZSh2YXIsIGZpbGUgPSAiQ0kuUkRhdGEiKQpgYGAKClRoZW4sIHdlIHdyYW5nbGUgdGhlIHJlc3VsdHMgYW5kIHBsb3QgdGhlbS4gICAKV2Ugc3RhcnQgd2l0aCB0aGUgKipyYXcgTVNFKiogdmFsdWVzLgoKYGBge3IsIHNpemUgPSAiZm9vdG5vdGVzaXplIn0KZGF0YV9nIDwtIGJpbmRfcm93cyh2YWwsIENJKSAlPiUKICAgIHBpdm90X3dpZGVyKG5hbWVzX2Zyb20gPSAiaW5kaWNhdG9yIiwgdmFsdWVzX2Zyb20gPSAidmFsdWUiKSAlPiUKICAgIG11dGF0ZShsb3dlciA9IE1TRSAtIENJLCB1cHBlciA9IE1TRSArIENJKSAlPiUKICAgIHBpdm90X2xvbmdlcigzOjYsIG5hbWVzX3RvID0gImluZGljYXRvciIsIHZhbHVlc190byA9ICJ2YWx1ZSIpCnBsb3RfTVNFX24gPC0gZGF0YV9nICU+JQogICAgZ2dwbG90KGFlcyh4ID0gbi8xMDAwLCB5ID0gdmFsdWUsIGNvbG9yID0gdHlwZSkpICsgCiAgICBnZW9tX2xpbmUoZGF0YSA9IGRhdGFfZyAlPiUgZmlsdGVyKGluZGljYXRvciA9PSAiTVNFIikpICsgCiAgICBnZW9tX2xpbmUoZGF0YSA9IGRhdGFfZyAlPiUgZmlsdGVyKGluZGljYXRvciA9PSAibG93ZXIiKSwgbGluZXR5cGUgPSAyKSArCiAgICBnZW9tX2xpbmUoZGF0YSA9IGRhdGFfZyAlPiUgZmlsdGVyKGluZGljYXRvciA9PSAidXBwZXIiKSwgbGluZXR5cGUgPSAzKSArCiAgICB4bGFiKFRlWCgiXFx0ZXh0YmZ7UG9pbnRzIChpbiB0aG91c2FuZHMpfSIpKSArIHlsYWIoVGVYKCJcXHRleHRiZntNZWFuIFNxdWFyZWQgRXJyb3J9IikpICsKICAgICNzY2FsZV9jb2xvcl92aXJpZGlzKG9wdGlvbj0iaW5mZXJubyIsIGJlZ2luID0gMC40LCBlbmQgPSAwLjksIGRpc2NyZXRlID0gVFJVRSkKICAgIHNjYWxlX2NvbG9yX21hbnVhbChuYW1lID0gIlR5cGUiLCAKICAgICAgICAgICAgICAgICAgICAgICBsYWJlbHMgPSBjKCJQcm9jZXNzIiwgIkludGVncmFsIiwgIk11bHRpdmFyaWF0ZSIpLCAKICAgICAgICAgICAgICAgICAgICAgICB2YWx1ZXMgPSBjKCIjMEI1NkIwIiwgIiNGRDY5MDkiLCAiIzEyQTc0QyIpKSArCiAgICBzY2FsZV94X2xvZzEwKCkgKwogICAgdGhlbWUoYXNwZWN0LnJhdGlvID0gMS4yLCBsZWdlbmQucG9zaXRpb24gPSBjKDAuOCwgMC4yKSkKcGxvdF9NU0VfbgpgYGAKCldlIHNlZSB0aGF0ICoqTW9udGUtQ2FybG8qKiBzaW11bGF0aW9ucywgYXMgZXhwZWN0ZWQsIHlpZWxkIHZhbHVlcyB0aGF0IG9zY2lsbGF0ZSBhIGxvdC4gVGhlIHNwZWVkIG9mIGNvbnZlcmdlbmNlIGlzIHByb3BvcnRpb25hbCB0byAkbl57LTEvMn0kLCBzbyBpbiBvdXIgY2FzZSwgdGhlIGVycm9yIGhhcyBhbiBvcmRlciBvZiBtYWduaXR1ZGUgb2YgJDEwXnstM30kLCB3aGljaCBpcyB3aGF0IHdlIG9ic2VydmUuCgpOZXh0LCB3ZSB0dXJuIHRvICoqY29tcHV0YXRpb24gdGltZXMqKi4KCmBgYHtyLCBzaXplID0gImZvb3Rub3Rlc2l6ZSJ9CnBsb3RfQ1BVX24gPC0gY3B1ICU+JQogICAgZ2dwbG90KGFlcyh4ID0gbi8xMDAwLCB5ID0gdmFsdWUsIGNvbG9yID0gdHlwZSkpICsgZ2VvbV9saW5lKCkgKwogICAgeGxhYihUZVgoIlxcdGV4dGJme1BvaW50cyAoaW4gdGhvdXNhbmRzKX0iKSkgKyB5bGFiKFRlWCgiXFx0ZXh0YmZ7Q1BVIHRpbWUgKHNlY29uZHMpIC0gbG9nc2NhbGV9IikpICsKICAgIHNjYWxlX3lfbG9nMTAoKSArCiAgICBzY2FsZV9jb2xvcl9tYW51YWwobmFtZSA9ICJUeXBlIiwgCiAgICAgICAgICAgICAgICAgICAgICAgbGFiZWxzID0gYygiUHJvY2VzcyIsICJJbnRlZ3JhbCIsICJNdWx0aXZhcmlhdGUiKSwgCiAgICAgICAgICAgICAgICAgICAgICAgdmFsdWVzID0gYygiIzBCNTZCMCIsICIjRkQ2OTA5IiwgIiMxMkE3NEMiKSkgKwogICAgdGhlbWUoYXNwZWN0LnJhdGlvID0gMS4yLCBsZWdlbmQucG9zaXRpb24gPSBjKDAuOCwgMC4yKSkKcGxvdF9DUFVfbgpgYGAKClRoZXJlIGlzIGFsbW9zdCBhIDEwLWZvbGQgZGlmZmVyZW50IGJldHdlZW4gZWFjaCBjb2xvciEgICAKCkJlbG93LCBpbiBvcmRlciB0byBzYXZlIHRpbWUsIHdlIGRpc2NhcmQgdGhlIHNsb3dlc3QgbWV0aG9kLgoKIyBTYXZlIGJvdGggcGxvdHMgdG9nZXRoZXIKCmBgYHtyLCBzaXplID0gImZvb3Rub3Rlc2l6ZSJ9CnBsb3RfZ3JpZChwbG90X01TRV9uLCBwbG90X0NQVV9uLCBuY29sID0gMiwgbnJvdyA9IDEpCiNnZ3NhdmUocGFzdGUoZm9sZGVyLCAiY29tcGFyaXNvbi5wZGYiLCBzZXA9Jy8nKSkKYGBgCgojIEltcGFjdCBvZiBUCgpOZXh0LCB3ZSBzd2l0Y2ggdG8gb3RoZXIgdmFsdWVzIG9mIFQuIEl0J3MgaW50ZXJlc3RpbmcgdG8gc2VlIHdoYXQgaGFwcGVucyB3aGVuIHRoZSBzYW1wbGUgc2l6ZSBhdWdtZW50cy4gICAKQWdhaW4sIHdlIHJlc29ydCB0byBhIGxvb3AuCgpgYGB7ciwgc2l6ZSA9ICJmb290bm90ZXNpemUifQpUVFQgPC0gc2VxKDUsMTAwLDUpCnZhbHVlX0FSX3NpbSA8LSAwCnZhbHVlX2ludGVncmFsX2luZCA8LSAwCmNwdV9BUl9zaW0gPC0gMApjcHVfaW50ZWdyYWxfaW5kIDwtIDAKZm9yKGkgaW4gMTpsZW5ndGgoVFRUKSl7CiAgICB0aWN0b2M6OnRpYygpCiAgICB2YWx1ZV9BUl9zaW1baV0gPC0gcXVhZF9lcnJfQ18yZDIoYV94LCBhX3ksIHJfeCwgcl95LCBzX3gsIHNfeSwgY29yLCBUVFRbaV0sIGssIG5fc2ltKVsxXQogICAgeiA8LSB0aWN0b2M6OnRvYyhxdWlldCA9IFQpCiAgICBjcHVfQVJfc2ltW2ldIDwtIHokdG9jIC0geiR0aWMKCiAgICB0aWN0b2M6OnRpYygpCiAgICB2YWx1ZV9pbnRlZ3JhbF9pbmRbaV0gPC0gTVNFX2ludGVncmFsX2luZChyX3gsIHJfeSwgc195LCBUVFRbaV0sIGssIG5fcG9pbnRzLCAxMDAgKiByb3VuZChsb2cobl9wb2ludHMpKSApCiAgICB6IDwtIHRpY3RvYzo6dG9jKHF1aWV0ID0gVCkKICAgIGNwdV9pbnRlZ3JhbF9pbmRbaV0gPC0geiR0b2MgLSB6JHRpYwp9Cgp2YWx1ZXNfVFQgPC0gZGF0YS5mcmFtZShUVFQsIHZhbHVlX0FSX3NpbSwgdmFsdWVfaW50ZWdyYWxfaW5kKSAlPiUKICAgIHBpdm90X2xvbmdlcigtVFRULCBuYW1lc190byA9ICJ0eXBlIiwgdmFsdWVzX3RvID0gInZhbHVlIikKc2F2ZSh2YWx1ZXNfVFQsIGZpbGUgPSAidmFsdWVzX1RULlJEYXRhIikKCmNwdV9UVCA8LSBkYXRhLmZyYW1lKFRUVCwgY3B1X0FSX3NpbSwgY3B1X2ludGVncmFsX2luZCkgJT4lCiAgICBwaXZvdF9sb25nZXIoLVRUVCwgbmFtZXNfdG8gPSAidHlwZSIsIHZhbHVlc190byA9ICJ2YWx1ZSIpCnNhdmUoY3B1X1RULCBmaWxlID0gImNwdV9UVC5SRGF0YSIpCmBgYAoKVGhlbiB3ZSBhbmFseXplIHRoZSByZXN1bHRzLgoKYGBge3IsIHNpemUgPSAiZm9vdG5vdGVzaXplIn0KcGxvdF9NU0VfVCA8LSB2YWx1ZXNfVFQgJT4lCiAgICBnZ3Bsb3QoYWVzKHggPSBUVFQsIHkgPSB2YWx1ZSwgY29sb3IgPSB0eXBlKSkgKyBnZW9tX2xpbmUoKSArIAogICAgeGxhYihUZVgoIlxcdGV4dGJme1R9IikpICsgeWxhYihUZVgoIlxcdGV4dGJme01lYW4gU3F1YXJlZCBFcnJvcn0iKSkgKwogICAgI3NjYWxlX2NvbG9yX3ZpcmlkaXMob3B0aW9uPSJpbmZlcm5vIiwgYmVnaW4gPSAwLjQsIGVuZCA9IDAuOSwgZGlzY3JldGUgPSBUUlVFKQogICAgc2NhbGVfY29sb3JfbWFudWFsKG5hbWUgPSAiVHlwZSIsIAogICAgICAgICAgICAgICAgICAgICAgIGxhYmVscyA9IGMoIlByb2Nlc3MiLCAiSW50ZWdyYWwiKSwgCiAgICAgICAgICAgICAgICAgICAgICAgdmFsdWVzID0gYygiIzBCNTZCMCIsICIjRkQ2OTA5IikpICsKICAgIHRoZW1lKGFzcGVjdC5yYXRpbyA9IDEuMiwgbGVnZW5kLnBvc2l0aW9uID0gYygwLjgsIDAuNikpCnBsb3RfTVNFX1QKYGBgCgpXaGVuIFQgaW5jcmVhc2VzLCB0aGUgTVNFIGZvbGxvd3MuIFRoZSByYW5nZSBvZiAkeSQgdmFsdWVzIG1ha2VzIGl0IGhhcmQgdG8gZGlzY2VybiB0aGUgZGlmZmVyZW5jZSBiZXR3ZWVuIHRoZSB0d28gY3VydmVzLgoKCmBgYHtyLCBzaXplID0gImZvb3Rub3Rlc2l6ZSJ9CnBsb3RfQ1BVX1QgPC0gY3B1X1RUICU+JQogICAgZ2dwbG90KGFlcyh4ID0gVFRULCB5ID0gdmFsdWUsIGNvbG9yID0gdHlwZSkpICsgZ2VvbV9saW5lKCkgKwogICAgeGxhYihUZVgoIlxcdGV4dGJme1R9IikpICsgeWxhYihUZVgoIlxcdGV4dGJme0NQVSB0aW1lIChzZWNvbmRzKSAtIGxvZ3NjYWxlfSIpKSArCiAgICBzY2FsZV95X2xvZzEwKCkgKwogICAgc2NhbGVfY29sb3JfbWFudWFsKG5hbWUgPSAiVHlwZSIsIAogICAgICAgICAgICAgICAgICAgICAgIGxhYmVscyA9IGMoIlByb2Nlc3MiLCAiSW50ZWdyYWwiKSwgCiAgICAgICAgICAgICAgICAgICAgICAgdmFsdWVzID0gYygiIzBCNTZCMCIsICIjRkQ2OTA5IikpICsKICAgIHRoZW1lKGFzcGVjdC5yYXRpbyA9IDEuMiwgbGVnZW5kLnBvc2l0aW9uID0gYygwLjgsIDAuNikpCnBsb3RfQ1BVX1QKYGBgCgpPdmVyYWxsLCB0aGUgZGlzY3JldGl6ZWQgaW50ZWdyYWwgaXMgbW9yZSB0aGFuIDEwMCB0aW1lcyBmYXN0ZXIgdGhhdCB0aGUgTUMgc2ltdWxhdGlvbnMuCgpgYGB7ciwgc2l6ZSA9ICJmb290bm90ZXNpemUifQpwbG90X2dyaWQocGxvdF9NU0VfbiwgcGxvdF9DUFVfVCwgbmNvbCA9IDIsIG5yb3cgPSAxKQojZ2dzYXZlKCJjb21wYXJpc29uXzA5LnBkZiIpCmBgYAo=