Packages and settings
Our analyses in R depend on a few functions available in five R packages and are based on the tidyverse tools and programming style.
library(tidyverse)
library(deSolve)
library(ggthemes)
library(cowplot)
library(viridis)
theme_set(theme_light()) # sets theme_light() as global
Epidemic simulations
A function was created to facilitate the production of synthetic epidemic data based on a few arguments that should be provided by the user such as epidemic duration, time interval, apparent infection rate, initial inoculum and uncertainty in the measures (standard deviation).
logi_fun <- function(t, y, par) {
y <- y[1]
r <- par$r
dy = y*r*(1-y)
return(list(c(dy)))
}
logistic = function(N=10, dt=1, y0=0.01, r, sd=1, inf = 1){
time <- seq(0,N, by=dt)
w = numeric(length(time))
y <- numeric(length(time))
y[1] = y0
aa <- -1
bb <- 1
for (k in 1:(length(time) - 1)) {
# Constant
if(inf == 1){r[k+1] = r[k]}
# Increasing
if(inf == 2){r[k+1] = r[k]+0.0035}
# Decreasing
if(inf == 3){r[k+1] = r[k]-0.0035}
# Sinusoidal
if(inf == 4){r[k+1] = r[k]+ ((pi * cos((pi * (k - 1)) / 30)) / 360)}
# Random
rr <- aa + (bb - aa) * runif(1)
if(inf == 5){r[k+1] = r[k] + 0.05 * rr}
InitCond <- c(y[k])
steps = seq(time[k],time[k+1], by=dt)
parms <- list(r=r[k])
ode_logi <- ode(InitCond, steps, logi_fun, parms)
y[k + 1] = last(ode_logi[,2])
# y[k + 1] <- dt * (r[k] * y[k] * (1 - y[k])) + y[k]
}
for(i in 1:length(time)){
w[i] = rnorm(1,y[i],sd = sd*y[i]*(1-y[i]))
if(w[i] > 1){
w[i]=1
}
if(w [i] < 0){
w[i]=0
}
}
return(data.frame(time, Intensity = y, Randon_intensity = w, inf_rate = r))
}
PF-derived rate
A function was prepared to run the Particle Filter for estimating the ry parameter.
SIR_filter = function(model, Nparti, measures, time, guess_r, sd_meas, sd_par, sd_model, dt= 0.5){
realSmeas <- measures
# Initial guess for the estimation. Here we set as the first value of the synthetic measures vector
N = length(realSmeas)
y <- numeric(N)
r <- numeric(N)
dt = dt
Xinit <- realSmeas[1]
S <- Xinit
r[1] = guess_r
time = time
# particle number
Nparti <- Nparti
Nparti1 <- (1 / Nparti)
# Variables' and model's error
Smeas <- mean(realSmeas)
stdmeas <- 0.25 * Smeas*(1-Smeas)
# Synthetic data error
stdmodel <- 0.005 * S
# Model error
stdmodeldif <- sd_par
# "parameter"" error
# Creating the weights' vector
wparti <- numeric(Nparti)
# Creating the others vectors for the SIR-PF algorithm
sinti <- numeric(N)
sinti[1] <- r[1]
Snew <- numeric(N)
Snew[1] <- S
xestsir <- numeric(N)
xestsir[1] <- S
loop1 <- c(1:(length(time) - 1))
loop2 <- c(1:Nparti)
xpartires <- numeric(Nparti)
sintires <- numeric(Nparti)
wpartires <- numeric(Nparti)
xpartinew <- numeric(Nparti)
sintinew <- numeric(Nparti)
stdsint <- numeric(N)
stdsir <- numeric(N)
aa <- -1
bb <- 1
for (k in loop1) { # loop to every time point
for (l in loop2) { # loop to create the particles
# Rondomic particle for "intensity" :
Sold <- Snew[k] + rnorm(1)*stdmodel
# Rondomic particle for "parameter":
rr <- aa + (bb - aa) * runif(1)
sintold <- sinti[k] + rr * stdmodeldif
# Solving the direct problem for every particle
InitCond <- c(Sold)
steps <- seq(time[k], time[k + 1], by = dt)
parms <- list(r = sintold)
# Logistic
if(model == 1){
ode_logi <- ode(InitCond, steps, logi_fun, parms)
y[k + 1] = last(ode_logi[,2])
}
# gompertz
if(model == 2) {
ode_gompi =ode(InitCond, steps, gompi_fun, parms)
y[k + 1] = last(ode_gompi[,2])
}
if(model == 3) {
ode_mono =ode(InitCond, steps, mono_fun, parms)
y[k + 1] = last(ode_mono[,2])
}
xpartinew[l] <- y[k + 1]
sintinew[l] <- sintold
# Calculating the weigths
wparti[l] <- exp(-((xpartinew[l] - realSmeas[k+1]) / stdmeas)^2)
}
# Setting the weigths between 0 and 1
wtotal <- sum(wparti)
wpartin <- numeric(Nparti)
wpartin <- wparti / wtotal
# Resampling
cresa <- numeric(Nparti)
uresa <- numeric(Nparti)
cresa[1] <- wpartin[1]
for (i in 2:Nparti) {
cresa[i] <- cresa[i - 1] + wpartin[i]
}
iresa <- 1
uresa[1] <- runif(1) * Nparti1
for (j in 1:Nparti) {
uresa[j] <- uresa[1] + Nparti1 * (j - 1)
while (uresa[j] > cresa[iresa]) {
iresa <- iresa + 1
}
xpartires[j] <- xpartinew[iresa]
sintires[j] <- sintinew[iresa]
wpartires[j] <- Nparti1
}
Snew[k + 1] <- mean(xpartires)
sinti[k + 1] <- mean(sintires)
stdsint[k + 1] <- sd(sintires)
xestsir[k + 1] <- mean(xpartires)
stdsir[k + 1] <- sd(xpartires)
xpartiold <- xpartires
# Error atualization
stdmodeldif <- sd_par
stdmodel <- sd_model * Snew[k + 1]
stdmeas <- sd_meas * realSmeas[k + 1]*(1-realSmeas[k + 1])
}
lbdsiro <- sinti - 2.576 * stdsint
ubdsiro <- sinti + 2.576 * stdsint
lbdsir <- xestsir - 2.576 * stdsir
ubdsir <- xestsir + 2.576 * stdsir
final <- data.frame(time, realSmeas, xestsir, lbdsir, ubdsir, sinti, lbdsiro, ubdsiro)
return(final)
}
Simulation of time-varying r
We need to set the initial values for simulating ry of various temporal patterns.
logi_setup <- matrix(c(
"Constant", 0.2,
"Increasing", 0.05,
"Decreasing", 0.3,
"Sinusoidal", 0.2,
"Random", 0.2
),
nrow = 5,
ncol = ,
byrow = TRUE
)
We will now run the Particle Filter to obtain the estimates of both the measures and the parameters for each type of infection rate, time interval and noise. For such, we will use a for-loop approach to generate a dataframe. Noise is represented by j, time interval by k and ry pattern by i.
noise <- c(0.1, 0.25)
logistic_all3 <- data.frame()
for (j in 1:2) {
logistic_all2 <- data.frame()
for (k in seq(1, 10, by = 2)) {
logistic_all <- data.frame()
for (i in 1:5) {
set.seed(5)
data <- logistic(N = 60, dt = 0.5, y0 = 0.001, r = as.numeric(logi_setup[i, 2]), sd = noise[j], inf = i)
data <- data %>%
filter(time %in% c(seq(0, 60, by = k)))
data_logi <- data.frame(
infection_type = as.factor(logi_setup[i, 1]),
SIR_filter(
model = 1,
guess_r = as.numeric(logi_setup[i, 2]),
Nparti = 1000,
measures = data$Randon_intensity,
time = data$time,
sd_meas = 0.25,
sd_par = 0.15,
sd_model = 0.005
),
y = data$Intensity,
inf_rate = data$inf_rate
)
logistic_all <- logistic_all %>%
bind_rows(data_logi)
}
logistic_all <- logistic_all %>%
mutate(time_interval = k)
logistic_all2 <- logistic_all2 %>%
bind_rows(logistic_all)
}
logistic_all2 <- logistic_all2 %>%
mutate(noise = noise[j])
logistic_all3 <- logistic_all3 %>%
bind_rows(logistic_all2)
}
Visualizing the DPCs
Now that we produced the DPC data we can visualize each curve for the combination ry pattern, noise in the measure and time interval. But first we need to correct names of levels of factors which need special characters like alpha and delta.
logistic_all3 <- logistic_all3 %>%
mutate(noise2 = noise) %>%
mutate(noise = case_when(
noise == 0.10 ~ "\u03b1 = 0.10",
noise == 0.25 ~ "\u03b1 = 0.25"
)) %>%
mutate(time_interval2 = time_interval) %>%
mutate(time_interval = case_when(
time_interval == 1 ~ "\u0394t = 1",
time_interval == 3 ~ "\u0394t = 3",
time_interval == 5 ~ "\u0394t = 5",
time_interval == 7 ~ "\u0394t = 7",
time_interval == 9 ~ "\u0394t = 9"
))
head(logistic_all3)
We can then proceed to produce a panel of plots for each of the 50 DPCs.
logistic_all3 %>%
ggplot() +
geom_line(aes(time, realSmeas, color = infection_type),
size = 1.2
) +
facet_grid(time_interval + noise ~ infection_type) +
scale_fill_manual(values = "gray") +
scale_color_colorblind() +
labs(
x = "Time",
y = "Disease intensity"
) +
theme(
legend.position = "none", text = element_text(size = 16),
strip.text = element_text(color = "black"),
strip.background = element_rect(fill = "white")
) +
scale_y_continuous(breaks = seq(0, 1, 0.25))
ggsave("figs/logistic_noised.png", dpi = 300, height = 12, width = 8)
Now a similar panel depicting the simulated ry values (solid colored line) and the respective point-estimate (and respective 95% CI)using the particle filter method.
logistic_all3 %>%
ggplot() +
geom_ribbon(aes(time, ymin = (ubdsiro), ymax = (lbdsiro), fill = "Ic 99%"), alpha = 0.5, stat = "identity") +
geom_line(aes(time, inf_rate, color = infection_type),
size = 1.2
) +
geom_point(aes(time, sinti),
size = 2, alpha = 1
) +
facet_grid(time_interval + noise ~ infection_type, scales = "free_y") +
scale_fill_manual(values = "gray") +
scale_color_colorblind() +
labs(
x = "Time",
y = "Apparent infection rate"
) +
theme(
legend.position = "none", text = element_text(size = 14),
strip.text = element_text(color = "black"),
strip.background = element_rect(fill = "white")
) +
scale_y_continuous(breaks = seq(-2, 2, 0.2))
ggsave("figs/logistic_air.png", dpi = 300, height = 12, width = 8)
Besides the ry parameter estimation, the particle filter also estimates the measures y at each time point. Let’s have a look at these estimates (and respective 95%CI) together with the synthetic measures (the solid lines).
logistic_all3 %>%
ggplot() +
geom_ribbon(aes(time, ymin = (ubdsir), ymax = (lbdsir), fill = "Ic 99%"), alpha = 0.5, stat = "identity") +
geom_line(aes(time, y, color = infection_type),
size = 1.2
) +
geom_point(aes(time, xestsir),
size = 1.5, alpha = 0.7
) +
facet_grid(time_interval + noise ~ infection_type) +
scale_fill_manual(values = "gray") +
scale_color_colorblind() +
labs(
x = "Time",
y = "Disease intensity"
) +
theme(
legend.position = "none", text = element_text(size = 16),
strip.text = element_text(color = "black"),
strip.background = element_rect(fill = "white")
) +
scale_y_continuous(breaks = seq(0, 1, 0.25))
ggsave("figs/logistic_curve.png", dpi = 300, height = 12, width = 8)
Estimation error
The accuracy of the estimates of ry, or how close they were to the simulated ry was evaluated based on the mean squared error statistic. The code below will produce a dataframe with the respective RMSE for each epidemics.
RMSE_data <- logistic_all3 %>%
group_by(infection_type, time_interval2, noise) %>%
mutate(
rmsi = (inf_rate - sinti)^2,
maei = abs(inf_rate - sinti)
) %>%
summarise(RMS = sqrt((1 / (length(inf_rate))) * sum(rmsi, na.rm = T))) %>%
mutate(model = "Logistic")
acuracy_logi <- RMSE_data
head(acuracy_logi)
Logit-derived rate
The following equation is commonly used to obtain ry between two times, given the two measures are known.
\[ r_{i+1} = \frac {[ln(\frac {y_{i+1}}{1-y_{i+1}}) -ln(\frac {y_{i}}{1-y_{i}}) ]}{t_{i+1} - t_{i}} \]
We will calculate them all for each curve the same way we did for the PF-estimated parameters and then visualize.
calc_r_log <- logistic_all3 %>%
group_by(infection_type, time_interval, noise) %>%
mutate(r_calc = (log(realSmeas / (1 - realSmeas)) - log((lag(realSmeas, 1) / (1 - (lag(realSmeas, 1)))))) / (time - lag(time, 1))) %>%
mutate(model = "Logistic")
calculated_r <- calc_r_log
head(calculated_r %>%
ungroup() %>%
select(infection_type, time, noise, time_interval2, r_calc))
calculated_r %>%
ggplot() +
geom_line(aes(time, inf_rate, color = infection_type),
size = 1.2
) +
geom_point(aes(time, r_calc),
size = 2,
alpha = 1
) +
facet_grid(time_interval + noise ~ infection_type, scales = "free_y") +
scale_fill_viridis() +
scale_color_colorblind() +
labs(
x = "Time",
y = "Apparent infection rate"
) +
guides(color = guide_legend("none")) +
theme(
text = element_text(size = 14), legend.position = "none",
strip.text = element_text(color = "black"),
strip.background = element_rect(fill = "white")
)
## Warning: Removed 50 rows containing missing values (geom_point).
ggsave("figs/r_calc_logi.png", dpi = 300, height = 12, width = 8)
## Warning: Removed 50 rows containing missing values (geom_point).
Error
acuracy_calc <- calculated_r %>%
filter(r_calc != is.na(r_calc)) %>%
group_by(infection_type, time_interval2, noise) %>%
mutate(
rmsi = (inf_rate - r_calc)^2,
maei = abs(inf_rate - r_calc)
) %>%
summarise(RMS = sqrt((1 / (length(inf_rate))) * sum(rmsi, na.rm = T)))
head(acuracy_calc)
JSBBbmFseXNpcwoKIyMgUGFja2FnZXMgYW5kIHNldHRpbmdzCgpPdXIgYW5hbHlzZXMgaW4gUiBkZXBlbmQgb24gYSBmZXcgZnVuY3Rpb25zIGF2YWlsYWJsZSBpbiBmaXZlIFIgcGFja2FnZXMgYW5kIGFyZSBiYXNlZCBvbiB0aGUgdGlkeXZlcnNlIHRvb2xzIGFuZCBwcm9ncmFtbWluZyBzdHlsZS4gCgpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQpsaWJyYXJ5KHRpZHl2ZXJzZSkKbGlicmFyeShkZVNvbHZlKQpsaWJyYXJ5KGdndGhlbWVzKQpsaWJyYXJ5KGNvd3Bsb3QpCmxpYnJhcnkodmlyaWRpcykKdGhlbWVfc2V0KHRoZW1lX2xpZ2h0KCkpICMgc2V0cyB0aGVtZV9saWdodCgpIGFzIGdsb2JhbApgYGAKCgojIyBFcGlkZW1pYyBzaW11bGF0aW9ucwoKQSBmdW5jdGlvbiB3YXMgY3JlYXRlZCB0byBmYWNpbGl0YXRlIHRoZSBwcm9kdWN0aW9uIG9mIHN5bnRoZXRpYyBlcGlkZW1pYyBkYXRhIGJhc2VkIG9uIGEgZmV3IGFyZ3VtZW50cyB0aGF0IHNob3VsZCBiZSBwcm92aWRlZCBieSB0aGUgdXNlciBzdWNoIGFzIGVwaWRlbWljIGR1cmF0aW9uLCB0aW1lIGludGVydmFsLCBhcHBhcmVudCBpbmZlY3Rpb24gcmF0ZSwgaW5pdGlhbCBpbm9jdWx1bSBhbmQgdW5jZXJ0YWludHkgaW4gdGhlIG1lYXN1cmVzIChzdGFuZGFyZCBkZXZpYXRpb24pLgoKYGBge3J9CmxvZ2lfZnVuIDwtIGZ1bmN0aW9uKHQsIHksIHBhcikgewogIAogIHkgPC0geVsxXQogIHIgPC0gcGFyJHIKICBkeSA9IHkqciooMS15KQogIHJldHVybihsaXN0KGMoZHkpKSkKfQoKbG9naXN0aWMgPSBmdW5jdGlvbihOPTEwLCBkdD0xLCB5MD0wLjAxLCByLCBzZD0xLCBpbmYgPSAxKXsKICAKICB0aW1lIDwtIHNlcSgwLE4sIGJ5PWR0KQogIHcgPSBudW1lcmljKGxlbmd0aCh0aW1lKSkKICB5IDwtIG51bWVyaWMobGVuZ3RoKHRpbWUpKQogIHlbMV0gPSB5MAogIGFhIDwtIC0xCiAgYmIgPC0gMQogIGZvciAoayBpbiAxOihsZW5ndGgodGltZSkgLSAxKSkgewogICAgIyBDb25zdGFudAogICAgaWYoaW5mID09IDEpe3JbaysxXSA9IHJba119CiAgICAKICAgICMgSW5jcmVhc2luZwogICAgaWYoaW5mID09IDIpe3JbaysxXSA9IHJba10rMC4wMDM1fQogICAgCiAgICAjIERlY3JlYXNpbmcKICAgIGlmKGluZiA9PSAzKXtyW2srMV0gPSByW2tdLTAuMDAzNX0KICAgIAogICAgIyBTaW51c29pZGFsCiAgICBpZihpbmYgPT0gNCl7cltrKzFdID0gcltrXSsgKChwaSAqIGNvcygocGkgKiAoayAtIDEpKSAvIDMwKSkgLyAzNjApfQogICAgCiAgICAjIFJhbmRvbQogICAgcnIgPC0gYWEgKyAoYmIgLSBhYSkgKiBydW5pZigxKQogICAgaWYoaW5mID09IDUpe3JbaysxXSA9IHJba10gKyAwLjA1ICogcnJ9CiAgICAKICAgIEluaXRDb25kIDwtIGMoeVtrXSkKICAgIHN0ZXBzID0gc2VxKHRpbWVba10sdGltZVtrKzFdLCBieT1kdCkKICAgIHBhcm1zIDwtIGxpc3Qocj1yW2tdKQogICAgb2RlX2xvZ2kgPC0gb2RlKEluaXRDb25kLCBzdGVwcywgbG9naV9mdW4sIHBhcm1zKSAgCiAgICB5W2sgKyAxXSA9IGxhc3Qob2RlX2xvZ2lbLDJdKQogICMgIHlbayArIDFdIDwtIGR0ICogKHJba10gKiB5W2tdICogKDEgLSB5W2tdKSkgKyB5W2tdCiAgfQogIAogIAogIGZvcihpIGluIDE6bGVuZ3RoKHRpbWUpKXsKICAgIAogICAgd1tpXSA9IHJub3JtKDEseVtpXSxzZCA9IHNkKnlbaV0qKDEteVtpXSkpCiAgICAKICAgIGlmKHdbaV0gPiAxKXsKICAgICAgd1tpXT0xCiAgICB9CiAgICBpZih3IFtpXSA8IDApewogICAgICB3W2ldPTAKICAgIH0KICB9CiAgcmV0dXJuKGRhdGEuZnJhbWUodGltZSwgSW50ZW5zaXR5ID0geSwgUmFuZG9uX2ludGVuc2l0eSA9IHcsIGluZl9yYXRlID0gcikpCn0KYGBgCgojIyBQRi1kZXJpdmVkIHJhdGUgCgpBIGZ1bmN0aW9uIHdhcyBwcmVwYXJlZCB0byBydW4gdGhlIFBhcnRpY2xlIEZpbHRlciBmb3IgZXN0aW1hdGluZyB0aGUgX3JfPHN1Yj55PC9zdWI+IHBhcmFtZXRlci4KCgpgYGB7cn0KClNJUl9maWx0ZXIgPSBmdW5jdGlvbihtb2RlbCwgTnBhcnRpLCBtZWFzdXJlcywgdGltZSwgIGd1ZXNzX3IsIHNkX21lYXMsIHNkX3Bhciwgc2RfbW9kZWwsIGR0PSAwLjUpewogIHJlYWxTbWVhcyA8LSBtZWFzdXJlcwoKIyBJbml0aWFsIGd1ZXNzIGZvciB0aGUgZXN0aW1hdGlvbi4gSGVyZSB3ZSBzZXQgYXMgdGhlIGZpcnN0IHZhbHVlIG9mIHRoZSBzeW50aGV0aWMgbWVhc3VyZXMgdmVjdG9yCiAgTiA9IGxlbmd0aChyZWFsU21lYXMpCiAgeSA8LSBudW1lcmljKE4pCiAgciA8LSBudW1lcmljKE4pCiAgZHQgPSBkdAogIFhpbml0IDwtIHJlYWxTbWVhc1sxXQogIFMgPC0gWGluaXQKICByWzFdID0gZ3Vlc3NfcgogIHRpbWUgPSB0aW1lCiAgIyBwYXJ0aWNsZSBudW1iZXIKICAKICBOcGFydGkgPC0gTnBhcnRpCiAgTnBhcnRpMSA8LSAoMSAvIE5wYXJ0aSkKICAKICAjIFZhcmlhYmxlcycgYW5kIG1vZGVsJ3MgZXJyb3IKICAKICAKICBTbWVhcyA8LSBtZWFuKHJlYWxTbWVhcykKICBzdGRtZWFzIDwtIDAuMjUgKiBTbWVhcyooMS1TbWVhcykKICAjIFN5bnRoZXRpYyBkYXRhIGVycm9yCiAgc3RkbW9kZWwgPC0gMC4wMDUgKiBTCiAgIyBNb2RlbCBlcnJvcgogIHN0ZG1vZGVsZGlmIDwtIHNkX3BhciAKICAjICJwYXJhbWV0ZXIiIiBlcnJvcgogIAogIAogICMgQ3JlYXRpbmcgdGhlIHdlaWdodHMnIHZlY3RvcgogIAogIHdwYXJ0aSA8LSBudW1lcmljKE5wYXJ0aSkKICAKICAKICAjIENyZWF0aW5nIHRoZSBvdGhlcnMgdmVjdG9ycyBmb3IgdGhlIFNJUi1QRiBhbGdvcml0aG0KICAKICBzaW50aSA8LSBudW1lcmljKE4pCiAgc2ludGlbMV0gPC0gclsxXSAKICBTbmV3IDwtIG51bWVyaWMoTikKICBTbmV3WzFdIDwtIFMKICB4ZXN0c2lyIDwtIG51bWVyaWMoTikKICB4ZXN0c2lyWzFdIDwtIFMKICBsb29wMSA8LSBjKDE6KGxlbmd0aCh0aW1lKSAtIDEpKQogIGxvb3AyIDwtIGMoMTpOcGFydGkpCiAgCiAgeHBhcnRpcmVzIDwtIG51bWVyaWMoTnBhcnRpKQogIHNpbnRpcmVzIDwtIG51bWVyaWMoTnBhcnRpKQogIHdwYXJ0aXJlcyA8LSBudW1lcmljKE5wYXJ0aSkKICB4cGFydGluZXcgPC0gbnVtZXJpYyhOcGFydGkpCiAgc2ludGluZXcgPC0gbnVtZXJpYyhOcGFydGkpCiAgc3Rkc2ludCA8LSBudW1lcmljKE4pCiAgc3Rkc2lyIDwtIG51bWVyaWMoTikKICAKICBhYSA8LSAtMQogIGJiIDwtIDEKICBmb3IgKGsgaW4gbG9vcDEpIHsgIyBsb29wIHRvIGV2ZXJ5IHRpbWUgcG9pbnQKICAgIAogICAgZm9yIChsIGluIGxvb3AyKSB7ICMgbG9vcCB0byBjcmVhdGUgdGhlIHBhcnRpY2xlcwogICAgICAKICAgICAgIyBSb25kb21pYyBwYXJ0aWNsZSBmb3IgImludGVuc2l0eSIgOgogICAgICAKICAgICAgU29sZCA8LSBTbmV3W2tdICsgcm5vcm0oMSkqc3RkbW9kZWwKICAgICAgCiAgICAgICMgUm9uZG9taWMgcGFydGljbGUgZm9yICJwYXJhbWV0ZXIiOgogICAgICAKICAgICAgcnIgPC0gYWEgKyAoYmIgLSBhYSkgKiBydW5pZigxKQogICAgICBzaW50b2xkIDwtIHNpbnRpW2tdICsgcnIgKiBzdGRtb2RlbGRpZiAKICAgICAgCiAgICAgICMgU29sdmluZyB0aGUgZGlyZWN0IHByb2JsZW0gZm9yIGV2ZXJ5IHBhcnRpY2xlCiAgICAgIEluaXRDb25kIDwtIGMoU29sZCkKICAgICAgc3RlcHMgPC0gc2VxKHRpbWVba10sIHRpbWVbayArIDFdLCBieSA9IGR0KQogICAgICBwYXJtcyA8LSBsaXN0KHIgPSBzaW50b2xkKQogICAgICAgIAogICAgICAKICAgICAgICAgIyBMb2dpc3RpYwogICAgICAgICBpZihtb2RlbCA9PSAxKXsKICAgICAgICAgICBvZGVfbG9naSA8LSBvZGUoSW5pdENvbmQsIHN0ZXBzLCBsb2dpX2Z1biwgcGFybXMpICAKICAgICAgICAgICB5W2sgKyAxXSA9IGxhc3Qob2RlX2xvZ2lbLDJdKQogICAgICAgICAgIH0gICAgIAogICAgICAgICAjIGdvbXBlcnR6CiAgICAgICAgIGlmKG1vZGVsID09IDIpIHsKICAgICAgICAgICBvZGVfZ29tcGkgPW9kZShJbml0Q29uZCwgc3RlcHMsIGdvbXBpX2Z1biwgcGFybXMpICAKICAgICAgICAgICB5W2sgKyAxXSA9IGxhc3Qob2RlX2dvbXBpWywyXSkgCiAgICAgICAgIH0KICAgICAgIAogICAgICBpZihtb2RlbCA9PSAzKSB7CiAgICAgICAgb2RlX21vbm8gPW9kZShJbml0Q29uZCwgc3RlcHMsIG1vbm9fZnVuLCBwYXJtcykgIAogICAgICAgIHlbayArIDFdID0gbGFzdChvZGVfbW9ub1ssMl0pIAogICAgICB9CiAgICAgIAoKICAgICAgIHhwYXJ0aW5ld1tsXSA8LSB5W2sgKyAxXQogICAgICAKICAgICAgIHNpbnRpbmV3W2xdIDwtIHNpbnRvbGQKICAgICAgCiAgICAgICMgQ2FsY3VsYXRpbmcgdGhlIHdlaWd0aHMKICAgICAgCiAgICAgIHdwYXJ0aVtsXSA8LSBleHAoLSgoeHBhcnRpbmV3W2xdIC0gcmVhbFNtZWFzW2srMV0pIC8gc3RkbWVhcyleMikKICAgIH0KICAgIAogICAgIyBTZXR0aW5nIHRoZSB3ZWlndGhzIGJldHdlZW4gMCBhbmQgMQogICAgCiAgICB3dG90YWwgPC0gc3VtKHdwYXJ0aSkKICAgIAogICAgd3BhcnRpbiA8LSBudW1lcmljKE5wYXJ0aSkKICAgIHdwYXJ0aW4gPC0gd3BhcnRpIC8gd3RvdGFsCiAgICAKICAgIAogICAgIyBSZXNhbXBsaW5nCiAgICAKICAgIGNyZXNhIDwtIG51bWVyaWMoTnBhcnRpKQogICAgdXJlc2EgPC0gbnVtZXJpYyhOcGFydGkpCiAgICBjcmVzYVsxXSA8LSB3cGFydGluWzFdCiAgICAKICAgIGZvciAoaSBpbiAyOk5wYXJ0aSkgewogICAgICBjcmVzYVtpXSA8LSBjcmVzYVtpIC0gMV0gKyB3cGFydGluW2ldCiAgICB9CiAgICBpcmVzYSA8LSAxCiAgICB1cmVzYVsxXSA8LSBydW5pZigxKSAqIE5wYXJ0aTEKICAgIAogICAgZm9yIChqIGluIDE6TnBhcnRpKSB7CiAgICAgIHVyZXNhW2pdIDwtIHVyZXNhWzFdICsgTnBhcnRpMSAqIChqIC0gMSkKICAgICAgCiAgICAgIHdoaWxlICh1cmVzYVtqXSA+IGNyZXNhW2lyZXNhXSkgewogICAgICAgIGlyZXNhIDwtIGlyZXNhICsgMQogICAgICB9CiAgICAgIAogICAgICB4cGFydGlyZXNbal0gPC0geHBhcnRpbmV3W2lyZXNhXQogICAgICBzaW50aXJlc1tqXSA8LSBzaW50aW5ld1tpcmVzYV0KICAgICAgd3BhcnRpcmVzW2pdIDwtIE5wYXJ0aTEKICAgIH0KICAgIAogICAgU25ld1trICsgMV0gPC0gbWVhbih4cGFydGlyZXMpCiAgICBzaW50aVtrICsgMV0gPC0gbWVhbihzaW50aXJlcykKICAgIAogICAgc3Rkc2ludFtrICsgMV0gPC0gc2Qoc2ludGlyZXMpCiAgICB4ZXN0c2lyW2sgKyAxXSA8LSBtZWFuKHhwYXJ0aXJlcykKICAgIAogICAgc3Rkc2lyW2sgKyAxXSA8LSBzZCh4cGFydGlyZXMpCiAgICB4cGFydGlvbGQgPC0geHBhcnRpcmVzCiAgICAKICAgICMgRXJyb3IgYXR1YWxpemF0aW9uCiAgICAKICAgIHN0ZG1vZGVsZGlmIDwtIHNkX3BhciAKICAgIHN0ZG1vZGVsIDwtIHNkX21vZGVsICogU25ld1trICsgMV0KICAgIHN0ZG1lYXMgPC0gc2RfbWVhcyAqIHJlYWxTbWVhc1trICsgMV0qKDEtcmVhbFNtZWFzW2sgKyAxXSkKICB9CiAgCiAgbGJkc2lybyA8LSBzaW50aSAtIDIuNTc2ICogIHN0ZHNpbnQKICB1YmRzaXJvIDwtIHNpbnRpICsgMi41NzYgKiBzdGRzaW50CiAgbGJkc2lyIDwtIHhlc3RzaXIgLSAyLjU3NiAqIHN0ZHNpcgogIHViZHNpciA8LSB4ZXN0c2lyICsgMi41NzYgKiBzdGRzaXIKICAKICBmaW5hbCA8LSBkYXRhLmZyYW1lKHRpbWUsIHJlYWxTbWVhcywgeGVzdHNpciwgbGJkc2lyLCB1YmRzaXIsIHNpbnRpLCBsYmRzaXJvLCB1YmRzaXJvKQogIAogIHJldHVybihmaW5hbCkKICB9CgpgYGAKCiMjIFNpbXVsYXRpb24gb2YgdGltZS12YXJ5aW5nIHIgCgpXZSBuZWVkIHRvIHNldCB0aGUgaW5pdGlhbCB2YWx1ZXMgZm9yIHNpbXVsYXRpbmcgX3JfPHN1Yj55PC9zdWI+IG9mIHZhcmlvdXMgdGVtcG9yYWwgcGF0dGVybnMuCgpgYGB7cn0KbG9naV9zZXR1cCA8LSBtYXRyaXgoYygKICAiQ29uc3RhbnQiLCAwLjIsCiAgIkluY3JlYXNpbmciLCAwLjA1LAogICJEZWNyZWFzaW5nIiwgMC4zLAogICJTaW51c29pZGFsIiwgMC4yLAogICJSYW5kb20iLCAwLjIKKSwKbnJvdyA9IDUsCm5jb2wgPSAsCmJ5cm93ID0gVFJVRQopCmBgYAoKV2Ugd2lsbCBub3cgcnVuIHRoZSBQYXJ0aWNsZSBGaWx0ZXIgdG8gb2J0YWluIHRoZSBlc3RpbWF0ZXMgb2YgYm90aCB0aGUgbWVhc3VyZXMgYW5kIHRoZSBwYXJhbWV0ZXJzIGZvciBlYWNoIHR5cGUgb2YgaW5mZWN0aW9uIHJhdGUsIHRpbWUgaW50ZXJ2YWwgYW5kIG5vaXNlLiBGb3Igc3VjaCwgd2Ugd2lsbCB1c2UgYSBmb3ItbG9vcCBhcHByb2FjaCB0byBnZW5lcmF0ZSBhIGRhdGFmcmFtZS4gTm9pc2UgaXMgcmVwcmVzZW50ZWQgYnkgaiwgdGltZSBpbnRlcnZhbCBieSBrIGFuZCBfcl88c3ViPnk8L3N1Yj4gcGF0dGVybiBieSBpLiAKCmBgYHtyIHdhcm5pbmc9RkFMU0V9Cm5vaXNlIDwtIGMoMC4xLCAwLjI1KQoKbG9naXN0aWNfYWxsMyA8LSBkYXRhLmZyYW1lKCkKZm9yIChqIGluIDE6MikgewogIGxvZ2lzdGljX2FsbDIgPC0gZGF0YS5mcmFtZSgpCiAgZm9yIChrIGluIHNlcSgxLCAxMCwgYnkgPSAyKSkgewogICAgbG9naXN0aWNfYWxsIDwtIGRhdGEuZnJhbWUoKQogICAgZm9yIChpIGluIDE6NSkgewogICAgICBzZXQuc2VlZCg1KQogICAgICBkYXRhIDwtIGxvZ2lzdGljKE4gPSA2MCwgZHQgPSAwLjUsIHkwID0gMC4wMDEsIHIgPSBhcy5udW1lcmljKGxvZ2lfc2V0dXBbaSwgMl0pLCBzZCA9IG5vaXNlW2pdLCBpbmYgPSBpKQogICAgICBkYXRhIDwtIGRhdGEgJT4lCiAgICAgICAgZmlsdGVyKHRpbWUgJWluJSBjKHNlcSgwLCA2MCwgYnkgPSBrKSkpCiAgICAgIGRhdGFfbG9naSA8LSBkYXRhLmZyYW1lKAogICAgICAgIGluZmVjdGlvbl90eXBlID0gYXMuZmFjdG9yKGxvZ2lfc2V0dXBbaSwgMV0pLAogICAgICAgIFNJUl9maWx0ZXIoCiAgICAgICAgICBtb2RlbCA9IDEsCiAgICAgICAgICBndWVzc19yID0gYXMubnVtZXJpYyhsb2dpX3NldHVwW2ksIDJdKSwKICAgICAgICAgIE5wYXJ0aSA9IDEwMDAsCiAgICAgICAgICBtZWFzdXJlcyA9IGRhdGEkUmFuZG9uX2ludGVuc2l0eSwKICAgICAgICAgIHRpbWUgPSBkYXRhJHRpbWUsCiAgICAgICAgICBzZF9tZWFzID0gMC4yNSwKICAgICAgICAgIHNkX3BhciA9IDAuMTUsCiAgICAgICAgICBzZF9tb2RlbCA9IDAuMDA1CiAgICAgICAgKSwKICAgICAgICB5ID0gZGF0YSRJbnRlbnNpdHksCiAgICAgICAgaW5mX3JhdGUgPSBkYXRhJGluZl9yYXRlCiAgICAgICkKCiAgICAgIGxvZ2lzdGljX2FsbCA8LSBsb2dpc3RpY19hbGwgJT4lCiAgICAgICAgYmluZF9yb3dzKGRhdGFfbG9naSkKICAgIH0KCiAgICBsb2dpc3RpY19hbGwgPC0gbG9naXN0aWNfYWxsICU+JQogICAgICBtdXRhdGUodGltZV9pbnRlcnZhbCA9IGspCgogICAgbG9naXN0aWNfYWxsMiA8LSBsb2dpc3RpY19hbGwyICU+JQogICAgICBiaW5kX3Jvd3MobG9naXN0aWNfYWxsKQogIH0KICBsb2dpc3RpY19hbGwyIDwtIGxvZ2lzdGljX2FsbDIgJT4lCiAgICBtdXRhdGUobm9pc2UgPSBub2lzZVtqXSkKCiAgbG9naXN0aWNfYWxsMyA8LSBsb2dpc3RpY19hbGwzICU+JQogICAgYmluZF9yb3dzKGxvZ2lzdGljX2FsbDIpCn0KYGBgCgoKIyMgVmlzdWFsaXppbmcgdGhlIERQQ3MKCk5vdyB0aGF0IHdlIHByb2R1Y2VkIHRoZSBEUEMgZGF0YSB3ZSBjYW4gdmlzdWFsaXplIGVhY2ggY3VydmUgZm9yIHRoZSBjb21iaW5hdGlvbiBfcl88c3ViPnk8L3N1Yj4gcGF0dGVybiwgbm9pc2UgaW4gdGhlIG1lYXN1cmUgYW5kIHRpbWUgaW50ZXJ2YWwuIEJ1dCBmaXJzdCB3ZSBuZWVkIHRvIGNvcnJlY3QgbmFtZXMgb2YgbGV2ZWxzIG9mIGZhY3RvcnMgd2hpY2ggbmVlZCBzcGVjaWFsIGNoYXJhY3RlcnMgbGlrZSBhbHBoYSBhbmQgZGVsdGEuCgpgYGB7cn0KbG9naXN0aWNfYWxsMyA8LSBsb2dpc3RpY19hbGwzICU+JQogIG11dGF0ZShub2lzZTIgPSBub2lzZSkgJT4lCiAgbXV0YXRlKG5vaXNlID0gY2FzZV93aGVuKAogICAgbm9pc2UgPT0gMC4xMCB+ICJcdTAzYjEgPSAgMC4xMCIsCiAgICBub2lzZSA9PSAwLjI1IH4gIlx1MDNiMSA9ICAwLjI1IgogICkpICU+JQogIG11dGF0ZSh0aW1lX2ludGVydmFsMiA9IHRpbWVfaW50ZXJ2YWwpICU+JQogIG11dGF0ZSh0aW1lX2ludGVydmFsID0gY2FzZV93aGVuKAogICAgdGltZV9pbnRlcnZhbCA9PSAxIH4gIlx1MDM5NHQgPSAgMSIsCiAgICB0aW1lX2ludGVydmFsID09IDMgfiAiXHUwMzk0dCA9ICAzIiwKICAgIHRpbWVfaW50ZXJ2YWwgPT0gNSB+ICJcdTAzOTR0ID0gIDUiLAogICAgdGltZV9pbnRlcnZhbCA9PSA3IH4gIlx1MDM5NHQgPSAgNyIsCiAgICB0aW1lX2ludGVydmFsID09IDkgfiAiXHUwMzk0dCA9ICA5IgogICkpCgpoZWFkKGxvZ2lzdGljX2FsbDMpCmBgYAoKCldlIGNhbiB0aGVuIHByb2NlZWQgdG8gcHJvZHVjZSBhIHBhbmVsIG9mIHBsb3RzIGZvciBlYWNoIG9mIHRoZSA1MCBEUENzLiAKCmBgYHtyIGZpZy5oZWlnaHQ9MTAsIGZpZy53aWR0aD0xMH0KbG9naXN0aWNfYWxsMyAlPiUKICBnZ3Bsb3QoKSArCiAgZ2VvbV9saW5lKGFlcyh0aW1lLCByZWFsU21lYXMsIGNvbG9yID0gaW5mZWN0aW9uX3R5cGUpLAogICAgc2l6ZSA9IDEuMgogICkgKwoKICBmYWNldF9ncmlkKHRpbWVfaW50ZXJ2YWwgKyBub2lzZSB+IGluZmVjdGlvbl90eXBlKSArCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gImdyYXkiKSArCiAgc2NhbGVfY29sb3JfY29sb3JibGluZCgpICsKICBsYWJzKAogICAgeCA9ICJUaW1lIiwKICAgIHkgPSAiRGlzZWFzZSBpbnRlbnNpdHkiCiAgKSArCiAgdGhlbWUoCiAgICBsZWdlbmQucG9zaXRpb24gPSAibm9uZSIsIHRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE2KSwKICAgIHN0cmlwLnRleHQgPSBlbGVtZW50X3RleHQoY29sb3IgPSAiYmxhY2siKSwKICAgIHN0cmlwLmJhY2tncm91bmQgPSBlbGVtZW50X3JlY3QoZmlsbCA9ICJ3aGl0ZSIpCiAgKSArCiAgc2NhbGVfeV9jb250aW51b3VzKGJyZWFrcyA9IHNlcSgwLCAxLCAwLjI1KSkKZ2dzYXZlKCJmaWdzL2xvZ2lzdGljX25vaXNlZC5wbmciLCBkcGkgPSAzMDAsIGhlaWdodCA9IDEyLCB3aWR0aCA9IDgpCmBgYAoKCk5vdyBhIHNpbWlsYXIgcGFuZWwgZGVwaWN0aW5nIHRoZSBzaW11bGF0ZWQgX3JfPHN1Yj55PC9zdWI+IHZhbHVlcyAoc29saWQgY29sb3JlZCBsaW5lKSBhbmQgdGhlIHJlc3BlY3RpdmUgcG9pbnQtZXN0aW1hdGUgKGFuZCByZXNwZWN0aXZlIDk1JSBDSSl1c2luZyB0aGUgcGFydGljbGUgZmlsdGVyIG1ldGhvZC4KCgpgYGB7ciBmaWcuaGVpZ2h0PTEwLCBmaWcud2lkdGg9MTB9Cgpsb2dpc3RpY19hbGwzICU+JQogIGdncGxvdCgpICsKICBnZW9tX3JpYmJvbihhZXModGltZSwgeW1pbiA9ICh1YmRzaXJvKSwgeW1heCA9IChsYmRzaXJvKSwgZmlsbCA9ICJJYyA5OSUiKSwgYWxwaGEgPSAwLjUsIHN0YXQgPSAiaWRlbnRpdHkiKSArCiAgZ2VvbV9saW5lKGFlcyh0aW1lLCBpbmZfcmF0ZSwgY29sb3IgPSBpbmZlY3Rpb25fdHlwZSksCiAgICBzaXplID0gMS4yCiAgKSArCiAgZ2VvbV9wb2ludChhZXModGltZSwgc2ludGkpLAogICAgc2l6ZSA9IDIsIGFscGhhID0gMQogICkgKwogIGZhY2V0X2dyaWQodGltZV9pbnRlcnZhbCArIG5vaXNlIH4gaW5mZWN0aW9uX3R5cGUsIHNjYWxlcyA9ICJmcmVlX3kiKSArCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gImdyYXkiKSArCiAgc2NhbGVfY29sb3JfY29sb3JibGluZCgpICsKICBsYWJzKAogICAgeCA9ICJUaW1lIiwKICAgIHkgPSAiQXBwYXJlbnQgaW5mZWN0aW9uIHJhdGUiCiAgKSArCiAgdGhlbWUoCiAgICBsZWdlbmQucG9zaXRpb24gPSAibm9uZSIsIHRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE0KSwKICAgIHN0cmlwLnRleHQgPSBlbGVtZW50X3RleHQoY29sb3IgPSAiYmxhY2siKSwKICAgIHN0cmlwLmJhY2tncm91bmQgPSBlbGVtZW50X3JlY3QoZmlsbCA9ICJ3aGl0ZSIpCiAgKSArCiAgc2NhbGVfeV9jb250aW51b3VzKGJyZWFrcyA9IHNlcSgtMiwgMiwgMC4yKSkKCmdnc2F2ZSgiZmlncy9sb2dpc3RpY19haXIucG5nIiwgZHBpID0gMzAwLCBoZWlnaHQgPSAxMiwgd2lkdGggPSA4KQpgYGAKCkJlc2lkZXMgdGhlIF9yXzxzdWI+eTwvc3ViPiBwYXJhbWV0ZXIgZXN0aW1hdGlvbiwgdGhlIHBhcnRpY2xlIGZpbHRlciBhbHNvIGVzdGltYXRlcyB0aGUgbWVhc3VyZXMgX3lfIGF0IGVhY2ggdGltZSBwb2ludC4gTGV0J3MgaGF2ZSBhIGxvb2sgYXQgdGhlc2UgZXN0aW1hdGVzIChhbmQgcmVzcGVjdGl2ZSA5NSVDSSkgdG9nZXRoZXIgd2l0aCB0aGUgc3ludGhldGljIG1lYXN1cmVzICh0aGUgc29saWQgbGluZXMpLiAKCgpgYGB7ciBmaWcuaGVpZ2h0PTEwLCBmaWcud2lkdGg9MTB9CmxvZ2lzdGljX2FsbDMgJT4lCiAgZ2dwbG90KCkgKwogIGdlb21fcmliYm9uKGFlcyh0aW1lLCB5bWluID0gKHViZHNpciksIHltYXggPSAobGJkc2lyKSwgZmlsbCA9ICJJYyA5OSUiKSwgYWxwaGEgPSAwLjUsIHN0YXQgPSAiaWRlbnRpdHkiKSArCiAgZ2VvbV9saW5lKGFlcyh0aW1lLCB5LCBjb2xvciA9IGluZmVjdGlvbl90eXBlKSwKICAgIHNpemUgPSAxLjIKICApICsKICBnZW9tX3BvaW50KGFlcyh0aW1lLCB4ZXN0c2lyKSwKICAgIHNpemUgPSAxLjUsIGFscGhhID0gMC43CiAgKSArCiAgZmFjZXRfZ3JpZCh0aW1lX2ludGVydmFsICsgbm9pc2UgfiBpbmZlY3Rpb25fdHlwZSkgKwogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9ICJncmF5IikgKwogIHNjYWxlX2NvbG9yX2NvbG9yYmxpbmQoKSArCiAgbGFicygKICAgIHggPSAiVGltZSIsCiAgICB5ID0gIkRpc2Vhc2UgaW50ZW5zaXR5IgogICkgKwogIHRoZW1lKAogICAgbGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiLCB0ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNiksCiAgICBzdHJpcC50ZXh0ID0gZWxlbWVudF90ZXh0KGNvbG9yID0gImJsYWNrIiksCiAgICBzdHJpcC5iYWNrZ3JvdW5kID0gZWxlbWVudF9yZWN0KGZpbGwgPSAid2hpdGUiKQogICkgKwogIHNjYWxlX3lfY29udGludW91cyhicmVha3MgPSBzZXEoMCwgMSwgMC4yNSkpCmdnc2F2ZSgiZmlncy9sb2dpc3RpY19jdXJ2ZS5wbmciLCBkcGkgPSAzMDAsIGhlaWdodCA9IDEyLCB3aWR0aCA9IDgpCmBgYAoKCiMjIyBFc3RpbWF0aW9uIGVycm9yCgpUaGUgYWNjdXJhY3kgb2YgdGhlIGVzdGltYXRlcyBvZiBfcl88c3ViPnk8L3N1Yj4sIG9yIGhvdyBjbG9zZSB0aGV5IHdlcmUgdG8gdGhlIHNpbXVsYXRlZCBfcl88c3ViPnk8L3N1Yj4gd2FzIGV2YWx1YXRlZCBiYXNlZCBvbiB0aGUgbWVhbiBzcXVhcmVkIGVycm9yIHN0YXRpc3RpYy4gVGhlIGNvZGUgYmVsb3cgd2lsbCBwcm9kdWNlIGEgZGF0YWZyYW1lIHdpdGggdGhlIHJlc3BlY3RpdmUgUk1TRSBmb3IgZWFjaCBlcGlkZW1pY3MuCgpgYGB7ciB3YXJuaW5nPUZBTFNFfQpSTVNFX2RhdGEgPC0gbG9naXN0aWNfYWxsMyAlPiUKICBncm91cF9ieShpbmZlY3Rpb25fdHlwZSwgdGltZV9pbnRlcnZhbDIsIG5vaXNlKSAlPiUKICBtdXRhdGUoCiAgICBybXNpID0gKGluZl9yYXRlIC0gc2ludGkpXjIsCiAgICBtYWVpID0gYWJzKGluZl9yYXRlIC0gc2ludGkpCiAgKSAlPiUKICBzdW1tYXJpc2UoUk1TID0gc3FydCgoMSAvIChsZW5ndGgoaW5mX3JhdGUpKSkgKiBzdW0ocm1zaSwgbmEucm0gPSBUKSkpICU+JQogIG11dGF0ZShtb2RlbCA9ICJMb2dpc3RpYyIpCgphY3VyYWN5X2xvZ2kgPC0gUk1TRV9kYXRhCgpoZWFkKGFjdXJhY3lfbG9naSkKYGBgCgojIyBMb2dpdC1kZXJpdmVkIHJhdGUKClRoZSBmb2xsb3dpbmcgZXF1YXRpb24gaXMgY29tbW9ubHkgdXNlZCB0byBvYnRhaW4gX3JfPHN1Yj55PC9zdWI+IGJldHdlZW4gdHdvIHRpbWVzLCBnaXZlbiB0aGUgdHdvIG1lYXN1cmVzIGFyZSBrbm93bi4gCgokJCByX3tpKzF9ID0gXGZyYWMge1tsbihcZnJhYyB7eV97aSsxfX17MS15X3tpKzF9fSkgLWxuKFxmcmFjIHt5X3tpfX17MS15X3tpfX0pIF19e3Rfe2krMX0gLSB0X3tpfX0gJCQKCldlIHdpbGwgY2FsY3VsYXRlIHRoZW0gYWxsIGZvciBlYWNoIGN1cnZlIHRoZSBzYW1lIHdheSB3ZSBkaWQgZm9yIHRoZSBQRi1lc3RpbWF0ZWQgcGFyYW1ldGVycyBhbmQgdGhlbiB2aXN1YWxpemUuCgpgYGB7cn0KY2FsY19yX2xvZyA8LSBsb2dpc3RpY19hbGwzICU+JQogIGdyb3VwX2J5KGluZmVjdGlvbl90eXBlLCB0aW1lX2ludGVydmFsLCBub2lzZSkgJT4lCiAgbXV0YXRlKHJfY2FsYyA9IChsb2cocmVhbFNtZWFzIC8gKDEgLSByZWFsU21lYXMpKSAtIGxvZygobGFnKHJlYWxTbWVhcywgMSkgLyAoMSAtIChsYWcocmVhbFNtZWFzLCAxKSkpKSkpIC8gKHRpbWUgLSBsYWcodGltZSwgMSkpKSAlPiUKICBtdXRhdGUobW9kZWwgPSAiTG9naXN0aWMiKQoKIApjYWxjdWxhdGVkX3IgPC0gY2FsY19yX2xvZwpoZWFkKGNhbGN1bGF0ZWRfciAlPiUKICB1bmdyb3VwKCkgJT4lCiAgc2VsZWN0KGluZmVjdGlvbl90eXBlLCB0aW1lLCBub2lzZSwgdGltZV9pbnRlcnZhbDIsIHJfY2FsYykpCmBgYAoKCmBgYHtyIGZpZy5oZWlnaHQ9MTAsIGZpZy53aWR0aD0xMH0KCmNhbGN1bGF0ZWRfciAlPiUKICBnZ3Bsb3QoKSArCiAgZ2VvbV9saW5lKGFlcyh0aW1lLCBpbmZfcmF0ZSwgY29sb3IgPSBpbmZlY3Rpb25fdHlwZSksCiAgICBzaXplID0gMS4yCiAgKSArCiAgZ2VvbV9wb2ludChhZXModGltZSwgcl9jYWxjKSwKICAgIHNpemUgPSAyLAogICAgYWxwaGEgPSAxCiAgKSArCiAgZmFjZXRfZ3JpZCh0aW1lX2ludGVydmFsICsgbm9pc2UgfiBpbmZlY3Rpb25fdHlwZSwgc2NhbGVzID0gImZyZWVfeSIpICsKICBzY2FsZV9maWxsX3ZpcmlkaXMoKSArCiAgc2NhbGVfY29sb3JfY29sb3JibGluZCgpICsKICBsYWJzKAogICAgeCA9ICJUaW1lIiwKICAgIHkgPSAiQXBwYXJlbnQgaW5mZWN0aW9uIHJhdGUiCiAgKSArCiAgZ3VpZGVzKGNvbG9yID0gZ3VpZGVfbGVnZW5kKCJub25lIikpICsKICB0aGVtZSgKICAgIHRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE0KSwgbGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiLAogICAgc3RyaXAudGV4dCA9IGVsZW1lbnRfdGV4dChjb2xvciA9ICJibGFjayIpLAogICAgc3RyaXAuYmFja2dyb3VuZCA9IGVsZW1lbnRfcmVjdChmaWxsID0gIndoaXRlIikKICApCgpnZ3NhdmUoImZpZ3Mvcl9jYWxjX2xvZ2kucG5nIiwgZHBpID0gMzAwLCBoZWlnaHQgPSAxMiwgd2lkdGggPSA4KQpgYGAKCiMjIyBFcnJvcgoKYGBge3Igd2FybmluZz1GQUxTRX0KYWN1cmFjeV9jYWxjIDwtIGNhbGN1bGF0ZWRfciAlPiUKICBmaWx0ZXIocl9jYWxjICE9IGlzLm5hKHJfY2FsYykpICU+JQogIGdyb3VwX2J5KGluZmVjdGlvbl90eXBlLCB0aW1lX2ludGVydmFsMiwgbm9pc2UpICU+JQogIG11dGF0ZSgKICAgIHJtc2kgPSAoaW5mX3JhdGUgLSByX2NhbGMpXjIsCiAgICBtYWVpID0gYWJzKGluZl9yYXRlIC0gcl9jYWxjKQogICkgJT4lCiAgc3VtbWFyaXNlKFJNUyA9IHNxcnQoKDEgLyAobGVuZ3RoKGluZl9yYXRlKSkpICogc3VtKHJtc2ksIG5hLnJtID0gVCkpKQpoZWFkKGFjdXJhY3lfY2FsYykKYGBgCgo=
Copyright 2019 Alves & Del Ponte