Preparing the data
For simplicity the code used for fetching the cases_dataset is not printed, but is available in the embedded code.
First we will filter the data so we start modelling from the first recovered case.
N <- 10260893 # Portugal population, estimative for 2020
start <- min(which(cases_dataset$recuperados > 0))
end <- nrow(cases_dataset)
data <- cases_dataset[start:end, ]
data <- data %>%
mutate(
time = seq_len(end - start + 1) - 1, S = as.numeric(N - (casos_confirmados + recuperados + n_obitos)),
I = as.numeric(casos_confirmados), R = as.numeric(recuperados + n_obitos)
) %>%
select(time, S, I, R)
Initial SIR Model
Next, we compute the initial beta and gamma for the SIR Model.
S0 <- data$S[1]
I0 <- data$I[1]
R0 <- data$R[1]
S1 <- data$S[2]
I1 <- data$I[2]
R1 <- data$R[2]
S0_ <- S1 - S0
# S0_ = -beta*I0*S0/N
beta0 <- -(S0_ * N) / (I0 * S0)
I0_ <- I1 - I0
# I0_ = beta*I0*S0/N-gamma*I0
# gamma0 = (beta0*(I0*S0)/N -I0_)/I0
### OR
R0_ <- R1 - R0
# R0_ = gamma*I0
gamma0 <- R0_ / I0
beta0
[1] 0.4556289
[1] 0.00591716
This SIR function is a set of three differential equations that relates the S, I and R variables in respect with time:
\(\frac{dS}{dt} = - \frac{\beta I S}{N}\)
\(\frac{dI}{dt} = \frac{\beta I S}{N}- \gamma I\)
\(\frac{dR}{dt} = \gamma I\)
and for that we will use the ode() solver from package deSolve.
####### SIR Model #######
SIR <- function(time, state, pars) { # returns rate of change
with(as.list(c(state, pars)), {
dS <- -beta * S * I / N
dI <- beta * S * I / N - gamma * I
dR <- gamma * I
return(list(c(dS, dI, dR)))
})
}
##### Initial Parameters ########
state <- c(S = S0, I = I0, R = R0)
pars <- list(beta = beta0, gamma = gamma0)
time <- seq(0, 200, by = 1)
###### differential equations ########
out <- ode(y = state, times = time, func = SIR, parms = pars)
###### Initial Plot #############
oldpar <- par(las = 1, tcl = 0.5, mgp = c(3, 0.8, 0), bty = "l")
matplot(out[, 1], out[, -1],
type = "l", lty = 1:3, lwd = 2,
col = 1:3, xlab = "time, days", ylab = ""
)
legend("topright", c("S", "I", "R"),
lty = 1:3, lwd = 2, col = 1:4
)
minor.tick(2, 2, 0.7)
minor.tick(10, 10, 0.5)

Incremental SIR Model
Now we create a function that will fit the model to the available data using modFit() from package FME.
sir_step <- function(data, beta, gamma) {
state <- c(S = data$S[1], I = data$I[1], R = data$R[1])
time <- seq(0, 200, by = 1) ## output times
Objective <- function(x, parset = names(x)) {
pars[parset] <- x
out <- ode(y = state, times = time, func = SIR, parms = pars, method = method)
## Model cost
return(modCost(out, data))
}
Fit <- modFit(Objective, c(beta = beta0, gamma = gamma0))
Fit$par
R_0 <- Fit$par[1] / Fit$par[2]
R_0
out2 <- ode(y = state, times = time, func = SIR, parms = Fit$par, method = method)
return(list(beta = Fit$par[1], gamma = Fit$par[2], re = R_0, forecast = out2))
}
Here we simulate as if we get new data, and change de model accordingly.
res <- list(beta = beta0, gamma = gamma0)
invisible(saveGIF( # save all graphics in an animated gif
for (i in seq(2, nrow(data), by = 1)) {
be <- res$beta
ga <- res$gamma
slice <- 1:i
data_slice <- data[slice,]
res <- sir_step(data_slice, 0.5, 0.5)
if(i == 2) {
out <- res$forecast[1:150, ]
} else {
out[min(slice):150, ] <- res$forecast[min(slice):150, ]
}
oldpar <- par(las = 1, tcl = 0.5, mgp = c(3, 0.8, 0), bty = "l")
matplot(out[, 1], out[, -1],
type = "l", lty = 1:3, lwd = 2,
main = "Animated SIR",
col = 1:3, xlab = paste0("Time\n",
sprintf("R %6.2f, beta %.3f, gamma %.3f", res$re, res$beta, res$gamma)), ylab = ""
)
legend("topright", c("S", "I", "R"),
lty = 1:3, lwd = 2, col = 1:4
)
minor.tick(2, 2, 0.7)
minor.tick(10, 10, 0.5)
points(data_slice$R, pch = 16, col = "green")
points(data_slice$I, pch = 16, col = "red")
points(data_slice$S, pch = 16, col = "black")
par(oldpar)
},
"ani_sir.gif",
interval = 0.5, autobrowse = FALSE, ani.res = 96, ani.height = 500, ani.width = 800
))

For better view, here is a zoomed plot
res <- list(beta = beta0, gamma = gamma0)
invisible(saveGIF( # save all graphics in an animated gif
for (i in seq(2, nrow(data), by = 1)) {
be <- res$beta
ga <- res$gamma
slice <- 1:i
data_slice <- data[slice,]
res <- sir_step(data_slice, 0.5, 0.5)
if(i == 2) {
out <- res$forecast[1:150, ]
} else {
out[min(slice):150, ] <- res$forecast[min(slice):150, ]
}
oldpar <- par(las = 1, tcl = 0.5, mgp = c(3, 0.8, 0), bty = "l")
matplot(out[, 1], out[, -1],
type = "l", lty = 1:3, lwd = 2,
main = "Animated SIR Zoom",
col = 1:3, xlab = paste0("Time\n",
sprintf("R %6.2f, beta %.3f, gamma %.3f", res$re, res$beta, res$gamma)), ylab = "",
ylim = c(0, max(data$I)*1.1), xlim = c(0, nrow(data)*1.1)
)
legend("topright", c("S", "I", "R"),
lty = 1:3, lwd = 2, col = 1:4
)
minor.tick(2, 2, 0.7)
minor.tick(10, 10, 0.5)
points(data_slice$R, pch = 16, col = "green")
points(data_slice$I, pch = 16, col = "red")
points(data_slice$S, pch = 16, col = "black")
par(oldpar)
},
"ani_sir_zoom.gif",
interval = 0.5, autobrowse = FALSE, ani.res = 96, ani.height = 500, ani.width = 800
))

LS0tCnRpdGxlOiAiSEVBRFMgLSBISURBIC0gQ09NUFNUQVQ6IEFzc2lnbm1lbnQgMyIKb3V0cHV0OiAKICBodG1sX25vdGVib29rOiAKICAgIGZpZ19oZWlnaHQ6IDgKICAgIGZpZ193aWR0aDogMTAKICAgIGhpZ2hsaWdodDogcHlnbWVudHMKICAgIHRoZW1lOiB1bml0ZWQKICAgIHRvYzogeWVzCmF1dGhvcjogRnJhbmNpc2NvIEJpc2Nob2ZmCi0tLQoKIyMgSW5zdHJ1Y3Rpb25zCgpDb25zaWRlciB0aGUgU0lSIG1vZGVsIGRpc2N1c3NlZCBpbiBjbGFzcy4gVXNpbmcgYW5kIGFkYXB0aW5nIHRoZSBjb2RlIHByb3ZpZGVkLCBkZWxpdmVyIGEgcmVwb3J0IG9uIGRpZmZlcmVudCBzY2VuYXJpb3Mgc2ltdWxhdGVkIHVzaW5nIHBhcmFtZXRlcnMgdGhhdCBjb3VsZCBiZSBlc3RpbWF0ZWQgZnJvbToKCmEpIFBvcnR1Z3Vlc2UgZGF0YSAoYWxsKQoKYikgT3RoZXIgY291bnRyaWVzJyBkYXRhIChhbGwpCgpjKSBTZWxlY3RlZCBwZXJpb2RzIG9mIGRhdGEgKGUuZy4gdXNpbmcgbGFuZG1hcmsgd2luZG93cyBvciBzbGlkaW5nIHdpbmRvd3MgaW4gZGF0YSB1c2VkIHRvIGZpdCBiZXRhIGFuZCBnYW1tYSkKClN1Ym1pdHRlZCBhIFBERiBmaWxlIHdpdGggdGhlIHJlcG9ydCwgZXhwbGFpbmluZyBhbmQgaW50ZXJwcmV0aW5nIHRoZSBzaW11bGF0ZWQgc2NlbmFyaW8ocykuCgoKYGBge3Igc2V0dXAsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0UsIGluY2x1ZGU9RkFMU0V9CmtuaXRyOjpvcHRzX2NodW5rJHNldCh0aWR5Lm9wdHMgPSBsaXN0KHdpZHRoLmN1dG9mZiA9IDYwKSwgdGlkeSA9IFRSVUUpCmxpYnJhcnkoZGVTb2x2ZSkKbGlicmFyeShsdWJyaWRhdGUpCmxpYnJhcnkoSG1pc2MpCmxpYnJhcnkoRk1FKQpsaWJyYXJ5KGpzb25saXRlKQpsaWJyYXJ5KGRwbHlyKQpsaWJyYXJ5KGFuaW1hdGlvbikgIyBhbmltYXRlZCBnaWZzIQpgYGAKCmBgYHtyIGxvYWQgZGF0YXNldCwgaW5jbHVkZT1GQUxTRX0KIyBHZXQgZGF0YSBmcm9tIERHUyBpZiB0aGUgY2FjaGUgZmlsZSBpcyBhYnNlbnQKaWYgKCFmaWxlLmV4aXN0cygiLi9kYXRhL2Rncy5yZGEiKSkgewogIGRnc19jYXNlc191cmwgPC0gImh0dHBzOi8vc2VydmljZXMuYXJjZ2lzLmNvbS9DQ1ppR1NFUWJBeHhGVmgzL2FyY2dpcy9yZXN0L3NlcnZpY2VzL0NPVklEMTlQb3J0dWdhbF92aWV3L0ZlYXR1cmVTZXJ2ZXIvMC9xdWVyeT9mPWpzb24md2hlcmU9MT0xJnJldHVybkdlb21ldHJ5PWZhbHNlJnNwYXRpYWxSZWw9ZXNyaVNwYXRpYWxSZWxJbnRlcnNlY3RzJm91dEZpZWxkcz0qJm9yZGVyQnlGaWVsZHM9ZGF0YXJlbGF0b3JpbyBhc2MmcmVzdWx0T2Zmc2V0PTAmcmVzdWx0UmVjb3JkQ291bnQ9MTAwMCIKCiAgZGdzX2Nhc2VzIDwtIGZyb21KU09OKFVSTGVuY29kZShkZ3NfY2FzZXNfdXJsKSkKCiAgIyBzYXZlIGNhY2hlIGZpbGUKICBzYXZlKGRnc19jYXNlcywgZmlsZSA9ICIuL2RhdGEvZGdzLnJkYSIpCn0gZWxzZSB7CiAgIyBsb2FkIGNhY2hlIGZpbGUKICBsb2FkKCIuL2RhdGEvZGdzLnJkYSIpCn0KCgpjYXNlc19kYXRhc2V0IDwtIGRnc19jYXNlc1tbImZlYXR1cmVzIl1dW1siYXR0cmlidXRlcyJdXQpjb2xuYW1lcyA8LSBjKAogICJpZF9vYmpldG8iLCAiaWRfZ2xvYmFsIiwgImRhdGFfcmVsYXRvcmlvIiwgImNhc29zX2NvbmZpcm1hZG9zIiwKICAibl9vYml0b3MiLCAicmVjdXBlcmFkb3MiLCAiY2Fzb3Nfc3VzcGVpdG9zIiwgImNhc29zX21hc2N1bGlub3MiLAogICJjYXNvc19mZW1pbmlub3MiLCAiZ3JfZXRhcmlvXzBfOSIsICJncl9ldGFyaW9fMTBfMTkiLCAiZ3JfZXRhcmlvXzIwXzI5IiwKICAiZ3JfZXRhcmlvXzMwXzM5IiwgImdyX2V0YXJpb180MF80OSIsICJncl9ldGFyaW9fNTBfNTkiLCAiZ3JfZXRhcmlvXzYwXzY5IiwKICAiZ3JfZXRhcmlvXzcwXzc5IiwgImdyX2V0YXJpb184MF84OSIsICJncl9ldGFyaW9fOTBfOTkiLCAiY2Fzb3NfaW1wb3J0YWRvcyIsCiAgImNhc29zX2NvbnRhY3RvIiwgInNpbnRvbWFfZmVicmUiLCAic2ludG9tYV90b3NzZSIsICJzaW50b21hX2RvcmVzIiwKICAic2ludG9tYV9jZWZhbGVpYSIsICJzaW50b21hX2ZyYXF1ZXphIiwgImNyZWF0aW9uX2RhdGUiLCAiY3JlYXRvciIsICJlZGl0X2RhdGUiLAogICJlZGl0b3IiLCAiY2Fzb3Nfbm92b3MiLCAic2ludG9tYV9kaWZyZXNwaXJhdG9yaWEiLCAidWx0aW1vX3JlZ2lzdG8iLAogICJlc3RyYW5nZWlybyIsICJjYXNvc19hdGl2b3MiLCAiYWd1YXJkYV9yZXN1bHRhZG8iLCAiY29udGF0b3NfdmlnaWxhbmNpYSIsCiAgImNhZGVpYXNfdHJhbnNtaXNzYW8iLCAiY2Fzb3NfaW50ZXJuYWRvcyIsICJjYXNvc19pbnRlcm5hZG9zX3VjaSIKKQpuYW1lcyhjYXNlc19kYXRhc2V0KSA8LSBjb2xuYW1lcwpjYXNlc19kYXRhc2V0JGRhdGFfcmVsYXRvcmlvIDwtIGFzLkRhdGUoYXMuUE9TSVhjdChjYXNlc19kYXRhc2V0JGRhdGFfcmVsYXRvcmlvIC8gMTAwMCwgb3JpZ2luID0gIjE5NzAtMDEtMDEiKSkKY2FzZXNfZGF0YXNldCRlZGl0X2RhdGUgPC0gYXMuUE9TSVhjdChjYXNlc19kYXRhc2V0JGVkaXRfZGF0ZSAvIDEwMDAsIG9yaWdpbiA9ICIxOTcwLTAxLTAxIikKY2FzZXNfZGF0YXNldCRjcmVhdGlvbl9kYXRlIDwtIGFzLlBPU0lYY3QoY2FzZXNfZGF0YXNldCRjcmVhdGlvbl9kYXRlIC8gMTAwMCwgb3JpZ2luID0gIjE5NzAtMDEtMDEiKQoKIyByZW1vdmUgcm93cyB0aGF0IGhhdmUgYWxsIE5BIGluIGEgc2VsZWN0aW9uIG9mIGNvbHVtbnMKY2FzZXNfZGF0YXNldCA8LSBjYXNlc19kYXRhc2V0ICU+JSBmaWx0ZXJfYXQodmFycyhjYXNvc19jb25maXJtYWRvczpzaW50b21hX2ZyYXF1ZXphKSwgYW55X3ZhcnMoIWlzLm5hKC4pKSkKCmNhc2VzX2RhdGFzZXQgPC0gY2FzZXNfZGF0YXNldCAlPiUKICBzZWxlY3QoCiAgICBpZF9vYmpldG8sIGRhdGFfcmVsYXRvcmlvLCBjYXNvc19jb25maXJtYWRvcywgY2Fzb3Nfbm92b3MsCiAgICBjYXNvc19zdXNwZWl0b3MsIHJlY3VwZXJhZG9zLCBuX29iaXRvcywgY2Fzb3NfbWFzY3VsaW5vcywgY2Fzb3NfZmVtaW5pbm9zLAogICAgY2Fzb3NfaW1wb3J0YWRvcywgY2Fzb3NfY29udGFjdG8sIGVzdHJhbmdlaXJvLCBjYXNvc19hdGl2b3MsIGFndWFyZGFfcmVzdWx0YWRvLAogICAgZ3JfZXRhcmlvXzBfOSwgZ3JfZXRhcmlvXzEwXzE5LCBncl9ldGFyaW9fMjBfMjksIGdyX2V0YXJpb18zMF8zOSwKICAgIGdyX2V0YXJpb180MF80OSwgZ3JfZXRhcmlvXzUwXzU5LCBncl9ldGFyaW9fNjBfNjksIGdyX2V0YXJpb183MF83OSwKICAgIGdyX2V0YXJpb184MF84OSwgZ3JfZXRhcmlvXzkwXzk5LCBjb250YXRvc192aWdpbGFuY2lhLAogICAgY2Fzb3NfaW50ZXJuYWRvcywgY2Fzb3NfaW50ZXJuYWRvc191Y2ksIHNpbnRvbWFfZmVicmUsIHNpbnRvbWFfdG9zc2UsCiAgICBzaW50b21hX2RvcmVzLCBzaW50b21hX2NlZmFsZWlhLCBzaW50b21hX2ZyYXF1ZXphLCBzaW50b21hX2RpZnJlc3BpcmF0b3JpYQogICkgJT4lCiAgYXJyYW5nZShkYXRhX3JlbGF0b3JpbykgJT4lCiAgZGlzdGluY3RfYXQodmFycyhkYXRhX3JlbGF0b3JpbzpzaW50b21hX2RpZnJlc3BpcmF0b3JpYSksIC5rZWVwX2FsbCA9IFRSVUUpICU+JQogIG11dGF0ZShpZF9vYmpldG8gPSAxOm4oKSkKYGBgCgojIyBQcmVwYXJpbmcgdGhlIGRhdGEKCkZvciBzaW1wbGljaXR5IHRoZSBjb2RlIHVzZWQgZm9yIGZldGNoaW5nIHRoZSBgY2FzZXNfZGF0YXNldGAgaXMgbm90IHByaW50ZWQsIGJ1dCBpcyBhdmFpbGFibGUgaW4gdGhlIGVtYmVkZGVkIGNvZGUuCgpGaXJzdCB3ZSB3aWxsIGZpbHRlciB0aGUgZGF0YSBzbyB3ZSBzdGFydCBtb2RlbGxpbmcgZnJvbSB0aGUgZmlyc3QgcmVjb3ZlcmVkIGNhc2UuCgpgYGB7ciBkYXRhIHNpcn0KTiA8LSAxMDI2MDg5MyAjIFBvcnR1Z2FsIHBvcHVsYXRpb24sIGVzdGltYXRpdmUgZm9yIDIwMjAKCnN0YXJ0IDwtIG1pbih3aGljaChjYXNlc19kYXRhc2V0JHJlY3VwZXJhZG9zID4gMCkpCmVuZCA8LSBucm93KGNhc2VzX2RhdGFzZXQpCgpkYXRhIDwtIGNhc2VzX2RhdGFzZXRbc3RhcnQ6ZW5kLCBdCmRhdGEgPC0gZGF0YSAlPiUKICBtdXRhdGUoCiAgICB0aW1lID0gc2VxX2xlbihlbmQgLSBzdGFydCArIDEpIC0gMSwgUyA9IGFzLm51bWVyaWMoTiAtIChjYXNvc19jb25maXJtYWRvcyArIHJlY3VwZXJhZG9zICsgbl9vYml0b3MpKSwKICAgIEkgPSBhcy5udW1lcmljKGNhc29zX2NvbmZpcm1hZG9zKSwgUiA9IGFzLm51bWVyaWMocmVjdXBlcmFkb3MgKyBuX29iaXRvcykKICApICU+JQogIHNlbGVjdCh0aW1lLCBTLCBJLCBSKQpgYGAKCiMjIEluaXRpYWwgU0lSIE1vZGVsCgpOZXh0LCB3ZSBjb21wdXRlIHRoZSBpbml0aWFsIGJldGEgYW5kIGdhbW1hIGZvciB0aGUgU0lSIE1vZGVsLgoKYGBge3J9ClMwIDwtIGRhdGEkU1sxXQpJMCA8LSBkYXRhJElbMV0KUjAgPC0gZGF0YSRSWzFdCgpTMSA8LSBkYXRhJFNbMl0KSTEgPC0gZGF0YSRJWzJdClIxIDwtIGRhdGEkUlsyXQoKUzBfIDwtIFMxIC0gUzAKIyBTMF8gPSAtYmV0YSpJMCpTMC9OCmJldGEwIDwtIC0oUzBfICogTikgLyAoSTAgKiBTMCkKCgpJMF8gPC0gSTEgLSBJMAojIEkwXyA9IGJldGEqSTAqUzAvTi1nYW1tYSpJMAojIGdhbW1hMCA9IChiZXRhMCooSTAqUzApL04gLUkwXykvSTAKCiMjIyBPUgoKUjBfIDwtIFIxIC0gUjAKIyBSMF8gID0gZ2FtbWEqSTAKZ2FtbWEwIDwtIFIwXyAvIEkwCgpiZXRhMApnYW1tYTAKYGBgCgpUaGlzIFNJUiBmdW5jdGlvbiBpcyBhIHNldCBvZiB0aHJlZSBkaWZmZXJlbnRpYWwgZXF1YXRpb25zIHRoYXQgcmVsYXRlcyB0aGUgUywgSSBhbmQgUiB2YXJpYWJsZXMgaW4gcmVzcGVjdCB3aXRoIHRpbWU6CgokXGZyYWN7ZFN9e2R0fSA9IC0gXGZyYWN7XGJldGEgSSBTfXtOfSQKCiRcZnJhY3tkSX17ZHR9ID0gXGZyYWN7XGJldGEgSSBTfXtOfS0gXGdhbW1hIEkkCgokXGZyYWN7ZFJ9e2R0fSA9IFxnYW1tYSBJJAoKYW5kIGZvciB0aGF0IHdlIHdpbGwgdXNlIHRoZSBgb2RlKClgIHNvbHZlciBmcm9tIHBhY2thZ2UgYGRlU29sdmVgLgoKYGBge3IgZmlyc3QgbW9kZWwsIGZpZy5oZWlnaHQ9NywgZmlnLndpZHRoPTEwfQojIyMjIyMjIFNJUiBNb2RlbCAjIyMjIyMjClNJUiA8LSBmdW5jdGlvbih0aW1lLCBzdGF0ZSwgcGFycykgeyAjIHJldHVybnMgcmF0ZSBvZiBjaGFuZ2UKICB3aXRoKGFzLmxpc3QoYyhzdGF0ZSwgcGFycykpLCB7CiAgICBkUyA8LSAtYmV0YSAqIFMgKiBJIC8gTgogICAgZEkgPC0gYmV0YSAqIFMgKiBJIC8gTiAtIGdhbW1hICogSQogICAgZFIgPC0gZ2FtbWEgKiBJCiAgICByZXR1cm4obGlzdChjKGRTLCBkSSwgZFIpKSkKICB9KQp9CgojIyMjIyBJbml0aWFsIFBhcmFtZXRlcnMgIyMjIyMjIyMKc3RhdGUgPC0gYyhTID0gUzAsIEkgPSBJMCwgUiA9IFIwKQpwYXJzIDwtIGxpc3QoYmV0YSA9IGJldGEwLCBnYW1tYSA9IGdhbW1hMCkKdGltZSA8LSBzZXEoMCwgMjAwLCBieSA9IDEpCgojIyMjIyMgZGlmZmVyZW50aWFsIGVxdWF0aW9ucyAjIyMjIyMjIwpvdXQgPC0gb2RlKHkgPSBzdGF0ZSwgdGltZXMgPSB0aW1lLCBmdW5jID0gU0lSLCBwYXJtcyA9IHBhcnMpCgojIyMjIyMgSW5pdGlhbCBQbG90ICMjIyMjIyMjIyMjIyMKb2xkcGFyIDwtIHBhcihsYXMgPSAxLCB0Y2wgPSAwLjUsIG1ncCA9IGMoMywgMC44LCAwKSwgYnR5ID0gImwiKQptYXRwbG90KG91dFssIDFdLCBvdXRbLCAtMV0sCiAgdHlwZSA9ICJsIiwgbHR5ID0gMTozLCBsd2QgPSAyLAogIGNvbCA9IDE6MywgeGxhYiA9ICJ0aW1lLCBkYXlzIiwgeWxhYiA9ICIiCikKbGVnZW5kKCJ0b3ByaWdodCIsIGMoIlMiLCAiSSIsICJSIiksCiAgbHR5ID0gMTozLCBsd2QgPSAyLCBjb2wgPSAxOjQKKQptaW5vci50aWNrKDIsIDIsIDAuNykKbWlub3IudGljaygxMCwgMTAsIDAuNSkKcGFyKG9sZHBhcikKYGBgCgojIyBJbmNyZW1lbnRhbCBTSVIgTW9kZWwKCk5vdyB3ZSBjcmVhdGUgYSBmdW5jdGlvbiB0aGF0IHdpbGwgZml0IHRoZSBtb2RlbCB0byB0aGUgYXZhaWxhYmxlIGRhdGEgdXNpbmcgYG1vZEZpdCgpYCBmcm9tIHBhY2thZ2UgYEZNRWAuCgpgYGB7ciBzaXJfc3RlcCBmdW5jdGlvbn0Kc2lyX3N0ZXAgPC0gZnVuY3Rpb24oZGF0YSwgYmV0YSwgZ2FtbWEpIHsKCiAgc3RhdGUgPC0gYyhTID0gZGF0YSRTWzFdLCBJID0gZGF0YSRJWzFdLCBSID0gZGF0YSRSWzFdKQoKICB0aW1lIDwtIHNlcSgwLCAyMDAsIGJ5ID0gMSkgIyMgb3V0cHV0IHRpbWVzCiAgICAKICBPYmplY3RpdmUgPC0gZnVuY3Rpb24oeCwgcGFyc2V0ID0gbmFtZXMoeCkpIHsKICAgIHBhcnNbcGFyc2V0XSA8LSB4CiAgICBvdXQgPC0gb2RlKHkgPSBzdGF0ZSwgdGltZXMgPSB0aW1lLCBmdW5jID0gU0lSLCBwYXJtcyA9IHBhcnMsIG1ldGhvZCA9IG1ldGhvZCkKICAgICMjIE1vZGVsIGNvc3QKICAgIHJldHVybihtb2RDb3N0KG91dCwgZGF0YSkpCiAgfQogIAogIEZpdCA8LSBtb2RGaXQoT2JqZWN0aXZlLCBjKGJldGEgPSBiZXRhMCwgZ2FtbWEgPSBnYW1tYTApKQogIAogIEZpdCRwYXIKICBSXzAgPC0gRml0JHBhclsxXSAvIEZpdCRwYXJbMl0KICBSXzAKICAKICBvdXQyIDwtIG9kZSh5ID0gc3RhdGUsIHRpbWVzID0gdGltZSwgZnVuYyA9IFNJUiwgcGFybXMgPSBGaXQkcGFyLCBtZXRob2QgPSBtZXRob2QpCgogIHJldHVybihsaXN0KGJldGEgPSBGaXQkcGFyWzFdLCBnYW1tYSA9IEZpdCRwYXJbMl0sIHJlID0gUl8wLCBmb3JlY2FzdCA9IG91dDIpKQp9CgpgYGAKCkhlcmUgd2Ugc2ltdWxhdGUgYXMgaWYgd2UgZ2V0IG5ldyBkYXRhLCBhbmQgY2hhbmdlIGRlIG1vZGVsIGFjY29yZGluZ2x5LgoKYGBge3IgYW5pbWF0ZWQgc2lyLCBmaWcuaGVpZ2h0PTcsIGZpZy53aWR0aD0xMH0KCnJlcyA8LSBsaXN0KGJldGEgPSBiZXRhMCwgZ2FtbWEgPSBnYW1tYTApCgppbnZpc2libGUoc2F2ZUdJRiggIyBzYXZlIGFsbCBncmFwaGljcyBpbiBhbiBhbmltYXRlZCBnaWYKICBmb3IgKGkgaW4gc2VxKDIsIG5yb3coZGF0YSksIGJ5ID0gMSkpIHsKICAgIGJlIDwtIHJlcyRiZXRhCiAgICBnYSA8LSByZXMkZ2FtbWEKICAgIAogICAgc2xpY2UgPC0gMTppCiAgICAKICAgIGRhdGFfc2xpY2UgPC0gZGF0YVtzbGljZSxdCgogICAgcmVzIDwtIHNpcl9zdGVwKGRhdGFfc2xpY2UsIDAuNSwgMC41KQogICAgCiAgICBpZihpID09IDIpIHsKICAgICAgb3V0IDwtIHJlcyRmb3JlY2FzdFsxOjE1MCwgXQogICAgfSBlbHNlIHsKICAgICAgb3V0W21pbihzbGljZSk6MTUwLCBdIDwtIHJlcyRmb3JlY2FzdFttaW4oc2xpY2UpOjE1MCwgXQogICAgfQogICAgCiAgICBvbGRwYXIgPC0gcGFyKGxhcyA9IDEsIHRjbCA9IDAuNSwgbWdwID0gYygzLCAwLjgsIDApLCBidHkgPSAibCIpCiAgICBtYXRwbG90KG91dFssIDFdLCBvdXRbLCAtMV0sCiAgICAgIHR5cGUgPSAibCIsIGx0eSA9IDE6MywgbHdkID0gMiwKICAgICAgbWFpbiA9ICJBbmltYXRlZCBTSVIiLAogICAgICBjb2wgPSAxOjMsIHhsYWIgPSBwYXN0ZTAoIlRpbWVcbiIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc3ByaW50ZigiUiAlNi4yZiwgYmV0YSAlLjNmLCBnYW1tYSAlLjNmIiwgcmVzJHJlLCByZXMkYmV0YSwgcmVzJGdhbW1hKSksIHlsYWIgPSAiIgogICAgKQogICAgbGVnZW5kKCJ0b3ByaWdodCIsIGMoIlMiLCAiSSIsICJSIiksCiAgICAgIGx0eSA9IDE6MywgbHdkID0gMiwgY29sID0gMTo0CiAgICApCiAgICBtaW5vci50aWNrKDIsIDIsIDAuNykKICAgIG1pbm9yLnRpY2soMTAsIDEwLCAwLjUpCiAgICAKICAgIHBvaW50cyhkYXRhX3NsaWNlJFIsIHBjaCA9IDE2LCBjb2wgPSAiZ3JlZW4iKQogICAgcG9pbnRzKGRhdGFfc2xpY2UkSSwgcGNoID0gMTYsIGNvbCA9ICJyZWQiKQogICAgcG9pbnRzKGRhdGFfc2xpY2UkUywgcGNoID0gMTYsIGNvbCA9ICJibGFjayIpCiAgICAKICAgIHBhcihvbGRwYXIpCiAgfSwKICAiYW5pX3Npci5naWYiLAogIGludGVydmFsID0gMC41LCBhdXRvYnJvd3NlID0gRkFMU0UsIGFuaS5yZXMgPSA5NiwgYW5pLmhlaWdodCA9IDUwMCwgYW5pLndpZHRoID0gODAwCikpCmBgYAoKIVtdKGFuaV9zaXIuZ2lmKQoKRm9yIGJldHRlciB2aWV3LCBoZXJlIGlzIGEgem9vbWVkIHBsb3QKCmBgYHtyIGFuaW1hdGVkIHNpciB6b29tLCBmaWcuaGVpZ2h0PTcsIGZpZy53aWR0aD0xMH0KCnJlcyA8LSBsaXN0KGJldGEgPSBiZXRhMCwgZ2FtbWEgPSBnYW1tYTApCgppbnZpc2libGUoc2F2ZUdJRiggIyBzYXZlIGFsbCBncmFwaGljcyBpbiBhbiBhbmltYXRlZCBnaWYKICBmb3IgKGkgaW4gc2VxKDIsIG5yb3coZGF0YSksIGJ5ID0gMSkpIHsKICAgIGJlIDwtIHJlcyRiZXRhCiAgICBnYSA8LSByZXMkZ2FtbWEKICAgIAogICAgc2xpY2UgPC0gMTppCiAgICAKICAgIGRhdGFfc2xpY2UgPC0gZGF0YVtzbGljZSxdCgogICAgcmVzIDwtIHNpcl9zdGVwKGRhdGFfc2xpY2UsIDAuNSwgMC41KQogICAgCiAgICBpZihpID09IDIpIHsKICAgICAgb3V0IDwtIHJlcyRmb3JlY2FzdFsxOjE1MCwgXQogICAgfSBlbHNlIHsKICAgICAgb3V0W21pbihzbGljZSk6MTUwLCBdIDwtIHJlcyRmb3JlY2FzdFttaW4oc2xpY2UpOjE1MCwgXQogICAgfQogICAgCiAgICBvbGRwYXIgPC0gcGFyKGxhcyA9IDEsIHRjbCA9IDAuNSwgbWdwID0gYygzLCAwLjgsIDApLCBidHkgPSAibCIpCiAgICBtYXRwbG90KG91dFssIDFdLCBvdXRbLCAtMV0sCiAgICAgIHR5cGUgPSAibCIsIGx0eSA9IDE6MywgbHdkID0gMiwKICAgICAgbWFpbiA9ICJBbmltYXRlZCBTSVIgWm9vbSIsCiAgICAgIGNvbCA9IDE6MywgeGxhYiA9IHBhc3RlMCgiVGltZVxuIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzcHJpbnRmKCJSICU2LjJmLCBiZXRhICUuM2YsIGdhbW1hICUuM2YiLCByZXMkcmUsIHJlcyRiZXRhLCByZXMkZ2FtbWEpKSwgeWxhYiA9ICIiLAogICAgICB5bGltID0gYygwLCBtYXgoZGF0YSRJKSoxLjEpLCB4bGltID0gYygwLCBucm93KGRhdGEpKjEuMSkKICAgICkKICAgIGxlZ2VuZCgidG9wcmlnaHQiLCBjKCJTIiwgIkkiLCAiUiIpLAogICAgICBsdHkgPSAxOjMsIGx3ZCA9IDIsIGNvbCA9IDE6NAogICAgKQogICAgbWlub3IudGljaygyLCAyLCAwLjcpCiAgICBtaW5vci50aWNrKDEwLCAxMCwgMC41KQogICAgCiAgICBwb2ludHMoZGF0YV9zbGljZSRSLCBwY2ggPSAxNiwgY29sID0gImdyZWVuIikKICAgIHBvaW50cyhkYXRhX3NsaWNlJEksIHBjaCA9IDE2LCBjb2wgPSAicmVkIikKICAgIHBvaW50cyhkYXRhX3NsaWNlJFMsIHBjaCA9IDE2LCBjb2wgPSAiYmxhY2siKQogICAgCiAgICBwYXIob2xkcGFyKQogIH0sCiAgImFuaV9zaXJfem9vbS5naWYiLAogIGludGVydmFsID0gMC41LCBhdXRvYnJvd3NlID0gRkFMU0UsIGFuaS5yZXMgPSA5NiwgYW5pLmhlaWdodCA9IDUwMCwgYW5pLndpZHRoID0gODAwCikpCmBgYAoKIVtdKGFuaV9zaXJfem9vbS5naWYpCg==