Introduction
This is annotated code for the headache_discrete.R
file. For convenience, the code from that file is shown below (with comments removed):
N_t <- 500
P_t <- 0
incidence <- 0.05
recovery <- 0.9
dat <- NULL
timesteps <- 1:100
for (t in timesteps) {
N_t1 <- N_t - round(incidence*N_t) + round(recovery*P_t)
P_t1 <- P_t + round(incidence*N_t) - round(recovery*P_t)
dat <- rbind(dat, c(N_t1, P_t1))
N_t <- N_t1
P_t <- P_t1
}
matplot(dat, xlab="time", ylab="number of people", type="l", lty = 1)
legend("topright", col = 1:2, legend = c("no headache","headache"), lwd=1)
Setting parameters
The first four lines of the code just saves our parameters as easy-to-remember variables. N_t
is the number of unaffected people at time zero. P_t
is the number of affected people at time zero. The incidence
and recovery
variables just store the rate at which people transition from unaffected to affected or vice versa.
N_t <- 500
P_t <- 0
incidence <- 0.05
recovery <- 0.9
Storing data and time sequence
dat <- NULL
timesteps <- 1:100
Next, we create an empty (i.e., NULL
) variable called dat
, which we will use to store our data.
- Note that every time you increment through a
for()
loop, all variables created within the loop are re-created. Thus, if you created dat
inside of the for()
loop, it would only store the very last observation.
We also create some sequence of time steps that we can iterate through, timesteps
. The colon (:
) operator in R
is just a convenient way of making a vector of integers from start:stop
.
- Sequence generation is a very common operation in most programming languages. One can also use several other sequence generators. See the help files for
seq()
, seq_int()
, or seq_along()
for other sequence generators.
The model
Recall that this is a discrete time model, so we can run the model by incrementing steps rather than differentiating. Specifically, our model is:
\[
\begin{aligned}
N_{t+1} &= N_t - \beta N_t + \gamma P_t \\
P_{t+1} &= P_t - \gamma P_t + \beta N_t
\end{aligned}
\]
Where \(\beta\) and \(\gamma\) are the incidence
and recovery
rates, respectively. These equations are represented as the N_t1
and P_t1
lines, respectively.
for (t in timesteps) {
N_t1 <- N_t - round(incidence*N_t) + round(recovery*P_t)
P_t1 <- P_t + round(incidence*N_t) - round(recovery*P_t)
dat <- rbind(dat, c(N_t1, P_t1))
N_t <- N_t1
P_t <- P_t1
}
The for (t in timesteps)
line is how R
constructs a loop. We’ll get more into loops later, but if you want a quick tutorial on for()
loops, check out my slides for last year’s class. For now, we just need to know that everything inside the {
curly brackets}
will run as many times as there are objects in the timestep
variable we created earlier.
Like most functions, the dat <- rbind(dat, c(N_t1, P_t1))
line is easiest to understand if read inside out.
- So starting inside,
c(N_t1, P_t1)
will take the results from the current time step and create a two-item vector.
- The
rbind()
command simply performs glues two vectors together, by r
ow. So dat
will be on top, the c(N_t1, P_t1)
will be glued beneath it as a new row.
- We then assign this new object to the
dat
name so that we keep adding rows as we go through our for()
loop.
Finally, the last two lines inside the loop update the compartments of our model before the next iteration (turning time t+1
into time t
).
At the end of this loop, our dat
object will now have two columns and 100 rows.
Plotting it
Now we simply plot it. See help(matplot)
for more information. When you do not supply an x
argument to matplot()
it simply plots each column of the matrix on the y-axis with equal-spaced numbers on the x-axis. The axis labels can be changed by using xlab
and ylab
. The type of plot is a line plot so we specify line="l"
. And we want all the lines to be a solid (1
) line so we specify the linetypes by using lty = 1
.
The legend()
command adds a legend to our current plot in the "topright"
corner. We specify the colors as 1:2
referring to the first column and second column. Similarly, the legend text is in the same order as the columns. The line width is set to 1 using lwd=1
.
matplot(dat, xlab="time", ylab="number of people", type="l", lty = 1)
legend("topright", col = 1:2, legend = c("no headache","headache"), lwd=1)
It appears very little is happening in our plots after about time 10. We can limit the x-axis using xlim
in the matplot()
command. This option takes a vector of length two with the first being the lower bound and the second being the upper bound.
dat <- NULL
Modifying parameters and time sequence
Let’s try a different set of parameters and see if we can get something more interesting.
N_t <- 100000
P_t <- 0
incidence <- 0.075
recovery <- 0.15
timesteps <- 1:100
Then let’s clear out the dat
object again and re-run our loop.
dat <- NULL
for (t in timesteps) {
N_t1 <- N_t - round(incidence*N_t) + round(recovery*P_t)
P_t1 <- P_t + round(incidence*N_t) - round(recovery*P_t)
dat <- rbind(dat, c(N_t1, P_t1))
N_t <- N_t1
P_t <- P_t1
}
And plot the results:
matplot(dat, xlab="time", ylab="number of people", type="l", lty = 1)
legend("topright", col = 1:2, legend = c("no headache","headache"), lwd=1)
Now it looks like something is happening. Try other parameters on your own.
Adding births and deaths
Suppose we are interested in adding births and deaths to our model. Further, assume everybody can have a child, but no children are born with headaches (or affected). Our model would look like this:
\[
\begin{aligned}
N_{t+1} &= N_t - \beta N_t + \gamma P_t + bN_t + bP_t - dN_t \\
P_{t+1} &= P_t - \gamma P_t + \beta N_t - dP_t
\end{aligned}
\]
How would this look in our code? First, we would need some values for b
and d
:
b <- .0001
d <- .0002
Let’s also make our timeline longer so we can see the long-term dynamics of adding births and deaths.
timesteps <- 1:1000
Then we would need to update our compartments in the for()
loop.
dat <- NULL
for (t in timesteps) {
N_t1 <- N_t - round(incidence*N_t) + round(recovery*P_t) + b*N_t + b*P_t - d*N_t
P_t1 <- P_t + round(incidence*N_t) - round(recovery*P_t) - d*P_t
dat <- rbind(dat, c(N_t1, P_t1))
N_t <- N_t1
P_t <- P_t1
}
And plot the results:
matplot(dat, xlab="time", ylab="number of people", type="l", lty = 1)
legend("topright", col = 1:2, legend = c("no headache","headache"), lwd=1)
We see that we hit “headache equilibrium” rather quickly, but because our death rate is greater than our birth rate, our population is slowly dying off. Try other parameters to see what happens.
LS0tCnRpdGxlOiAiQW5ub3RhdGVkIGNvZGUgZm9yIGBoZWFkYWNoZV9kaXNjcmV0ZS5SYCIKYXV0aG9yOiAiTWF0aGV3IEtpYW5nIgpkYXRlOiAiYHIgZm9ybWF0KFN5cy50aW1lKCksICclQiAlZCwgJVknKWAiCm91dHB1dDogCiAgaHRtbF9ub3RlYm9vazoKICAgIHRvYzogdHJ1ZQotLS0KCiMjIEludHJvZHVjdGlvbgoKVGhpcyBpcyBhbm5vdGF0ZWQgY29kZSBmb3IgdGhlIFtgaGVhZGFjaGVfZGlzY3JldGUuUmBdKGh0dHBzOi8vcmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbS9ta2lhbmcvZXBpNTAxX3Jldmlldy9tYXN0ZXIvZXhlcmNpc2VzLzAxX2hlYWRhY2hlcy9oZWFkYWNoZV9kaXNjcmV0ZS5SKSBmaWxlLiBGb3IgY29udmVuaWVuY2UsIHRoZSBjb2RlIGZyb20gdGhhdCBmaWxlIGlzIHNob3duIGJlbG93ICh3aXRoIGNvbW1lbnRzIHJlbW92ZWQpOgoKYGBge3IgZXZhbD1GQUxTRSwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KTl90IDwtIDUwMCAgClBfdCA8LSAwIAppbmNpZGVuY2UgPC0gMC4wNSAKcmVjb3ZlcnkgPC0gMC45ICAKCmRhdCA8LSBOVUxMCnRpbWVzdGVwcyA8LSAxOjEwMCAgCgpmb3IgKHQgaW4gdGltZXN0ZXBzKSB7CiAgICBOX3QxIDwtIE5fdCAtIHJvdW5kKGluY2lkZW5jZSpOX3QpICsgcm91bmQocmVjb3ZlcnkqUF90KSAKCiAgICBQX3QxIDwtIFBfdCArIHJvdW5kKGluY2lkZW5jZSpOX3QpIC0gcm91bmQocmVjb3ZlcnkqUF90KQoKICAgIGRhdCA8LSByYmluZChkYXQsIGMoTl90MSwgUF90MSkpIAoKICAgIE5fdCA8LSBOX3QxIAogICAgUF90IDwtIFBfdDEgCn0KCm1hdHBsb3QoZGF0LCB4bGFiPSJ0aW1lIiwgeWxhYj0ibnVtYmVyIG9mIHBlb3BsZSIsIHR5cGU9ImwiLCBsdHkgPSAxKQpsZWdlbmQoInRvcHJpZ2h0IiwgY29sID0gMToyLCBsZWdlbmQgPSBjKCJubyBoZWFkYWNoZSIsImhlYWRhY2hlIiksIGx3ZD0xKQpgYGAKCiMjIFNldHRpbmcgcGFyYW1ldGVycwpUaGUgZmlyc3QgZm91ciBsaW5lcyBvZiB0aGUgY29kZSBqdXN0IHNhdmVzIG91ciBwYXJhbWV0ZXJzIGFzIGVhc3ktdG8tcmVtZW1iZXIgdmFyaWFibGVzLiBgTl90YCBpcyB0aGUgbnVtYmVyIG9mIHVuYWZmZWN0ZWQgcGVvcGxlIGF0IHRpbWUgemVyby4gYFBfdGAgaXMgdGhlIG51bWJlciBvZiBhZmZlY3RlZCBwZW9wbGUgYXQgdGltZSB6ZXJvLiBUaGUgYGluY2lkZW5jZWAgYW5kIGByZWNvdmVyeWAgdmFyaWFibGVzIGp1c3Qgc3RvcmUgdGhlIHJhdGUgYXQgd2hpY2ggcGVvcGxlIHRyYW5zaXRpb24gZnJvbSB1bmFmZmVjdGVkIHRvIGFmZmVjdGVkIG9yIHZpY2UgdmVyc2EuIAoKYGBge3J9Ck5fdCA8LSA1MDAgIApQX3QgPC0gMCAKaW5jaWRlbmNlIDwtIDAuMDUgCnJlY292ZXJ5IDwtIDAuOSAKYGBgCgojIyBTdG9yaW5nIGRhdGEgYW5kIHRpbWUgc2VxdWVuY2UKCmBgYHtyfQpkYXQgPC0gTlVMTAp0aW1lc3RlcHMgPC0gMToxMDAgIApgYGAKCk5leHQsIHdlIGNyZWF0ZSBhbiBlbXB0eSAoaS5lLiwgYE5VTExgKSB2YXJpYWJsZSBjYWxsZWQgYGRhdGAsIHdoaWNoIHdlIHdpbGwgdXNlIHRvIHN0b3JlIG91ciBkYXRhLiAKCi0gTm90ZSB0aGF0IGV2ZXJ5IHRpbWUgeW91IGluY3JlbWVudCB0aHJvdWdoIGEgYGZvcigpYCBsb29wLCBhbGwgdmFyaWFibGVzIGNyZWF0ZWQgd2l0aGluIHRoZSBsb29wIGFyZSByZS1jcmVhdGVkLiBUaHVzLCBpZiB5b3UgY3JlYXRlZCBgZGF0YCBpbnNpZGUgb2YgdGhlIGBmb3IoKWAgbG9vcCwgaXQgd291bGQgb25seSBzdG9yZSB0aGUgdmVyeSBsYXN0IG9ic2VydmF0aW9uLiAKCldlIGFsc28gY3JlYXRlIHNvbWUgc2VxdWVuY2Ugb2YgdGltZSBzdGVwcyB0aGF0IHdlIGNhbiBpdGVyYXRlIHRocm91Z2gsIGB0aW1lc3RlcHNgLiBUaGUgY29sb24gKGA6YCkgb3BlcmF0b3IgaW4gYFJgIGlzIGp1c3QgYSBjb252ZW5pZW50IHdheSBvZiBtYWtpbmcgYSB2ZWN0b3Igb2YgaW50ZWdlcnMgZnJvbSBgc3RhcnQ6c3RvcGAuCgotIFNlcXVlbmNlIGdlbmVyYXRpb24gaXMgYSB2ZXJ5IGNvbW1vbiBvcGVyYXRpb24gaW4gbW9zdCBwcm9ncmFtbWluZyBsYW5ndWFnZXMuICBPbmUgY2FuIGFsc28gdXNlIHNldmVyYWwgb3RoZXIgc2VxdWVuY2UgZ2VuZXJhdG9ycy4gU2VlIHRoZSBoZWxwIGZpbGVzIGZvciBgc2VxKClgLCBgc2VxX2ludCgpYCwgb3IgYHNlcV9hbG9uZygpYCBmb3Igb3RoZXIgc2VxdWVuY2UgZ2VuZXJhdG9ycy4KCiMjIFRoZSBtb2RlbApSZWNhbGwgdGhhdCB0aGlzIGlzIGEgZGlzY3JldGUgdGltZSBtb2RlbCwgc28gd2UgY2FuIHJ1biB0aGUgbW9kZWwgYnkgaW5jcmVtZW50aW5nIHN0ZXBzIHJhdGhlciB0aGFuIGRpZmZlcmVudGlhdGluZy4gU3BlY2lmaWNhbGx5LCBvdXIgbW9kZWwgaXM6CgokJApcYmVnaW57YWxpZ25lZH0KTl97dCsxfSAmPSBOX3QgLSBcYmV0YSBOX3QgKyBcZ2FtbWEgUF90IFxcClBfe3QrMX0gJj0gUF90IC0gXGdhbW1hIFBfdCArIFxiZXRhIE5fdApcZW5ke2FsaWduZWR9CiQkCgpXaGVyZSAkXGJldGEkIGFuZCAkXGdhbW1hJCBhcmUgdGhlIGBpbmNpZGVuY2VgIGFuZCBgcmVjb3ZlcnlgIHJhdGVzLCByZXNwZWN0aXZlbHkuIFRoZXNlIGVxdWF0aW9ucyBhcmUgcmVwcmVzZW50ZWQgYXMgdGhlIGBOX3QxYCBhbmQgYFBfdDFgIGxpbmVzLCByZXNwZWN0aXZlbHkuCmBgYHtyfQpmb3IgKHQgaW4gdGltZXN0ZXBzKSB7CiAgICBOX3QxIDwtIE5fdCAtIHJvdW5kKGluY2lkZW5jZSpOX3QpICsgcm91bmQocmVjb3ZlcnkqUF90KSAKCiAgICBQX3QxIDwtIFBfdCArIHJvdW5kKGluY2lkZW5jZSpOX3QpIC0gcm91bmQocmVjb3ZlcnkqUF90KQoKICAgIGRhdCA8LSByYmluZChkYXQsIGMoTl90MSwgUF90MSkpIAoKICAgIE5fdCA8LSBOX3QxIAogICAgUF90IDwtIFBfdDEgCn0KYGBgCgpUaGUgYGZvciAodCBpbiB0aW1lc3RlcHMpYCBsaW5lIGlzIGhvdyBgUmAgY29uc3RydWN0cyBhIGxvb3AuIFdlJ2xsIGdldCBtb3JlIGludG8gbG9vcHMgbGF0ZXIsIGJ1dCBpZiB5b3Ugd2FudCBhIHF1aWNrIHR1dG9yaWFsIG9uIGBmb3IoKWAgbG9vcHMsIGNoZWNrIG91dCBbbXkgc2xpZGVzIGZvciBsYXN0IHllYXIncyBjbGFzc10oaHR0cHM6Ly9ta2lhbmcuZ2l0aHViLmlvL2VwaTUwMV9yZXZpZXcvc3ByaW5nXzIwMTcvamFuMzAtdGltZXN0ZXBzLW1lYXNsZXMtc2Vpci9pbmRleC5odG1sIzc5KS4gRm9yIG5vdywgd2UganVzdCBuZWVkIHRvIGtub3cgdGhhdCBldmVyeXRoaW5nIGluc2lkZSB0aGUgYHtgY3VybHkgYnJhY2tldHNgfWAgd2lsbCBydW4gYXMgbWFueSB0aW1lcyBhcyB0aGVyZSBhcmUgb2JqZWN0cyBpbiB0aGUgYHRpbWVzdGVwYCB2YXJpYWJsZSB3ZSBjcmVhdGVkIGVhcmxpZXIuIAoKTGlrZSBtb3N0IGZ1bmN0aW9ucywgdGhlIGBkYXQgPC0gcmJpbmQoZGF0LCBjKE5fdDEsIFBfdDEpKWAgbGluZSBpcyBlYXNpZXN0IHRvIHVuZGVyc3RhbmQgaWYgcmVhZCBpbnNpZGUgb3V0LiAKCjEuIFNvIHN0YXJ0aW5nIGluc2lkZSwgYGMoTl90MSwgUF90MSlgIHdpbGwgdGFrZSB0aGUgcmVzdWx0cyBmcm9tIHRoZSBjdXJyZW50IHRpbWUgc3RlcCBhbmQgY3JlYXRlIGEgdHdvLWl0ZW0gdmVjdG9yLiAKMS4gVGhlIGByYmluZCgpYCBjb21tYW5kIHNpbXBseSBwZXJmb3JtcyBnbHVlcyB0d28gdmVjdG9ycyB0b2dldGhlciwgYnkgYHJgb3cuIFNvIGBkYXRgIHdpbGwgYmUgb24gdG9wLCB0aGUgYGMoTl90MSwgUF90MSlgIHdpbGwgYmUgZ2x1ZWQgYmVuZWF0aCBpdCBhcyBhIG5ldyByb3cuCjEuIFdlIHRoZW4gYXNzaWduIHRoaXMgbmV3IG9iamVjdCB0byB0aGUgYGRhdGAgbmFtZSBzbyB0aGF0IHdlIGtlZXAgYWRkaW5nIHJvd3MgYXMgd2UgZ28gdGhyb3VnaCBvdXIgYGZvcigpYCBsb29wLgoKRmluYWxseSwgdGhlIGxhc3QgdHdvIGxpbmVzIGluc2lkZSB0aGUgbG9vcCB1cGRhdGUgdGhlIGNvbXBhcnRtZW50cyBvZiBvdXIgbW9kZWwgYmVmb3JlIHRoZSBuZXh0IGl0ZXJhdGlvbiAodHVybmluZyB0aW1lIGB0KzFgIGludG8gdGltZSBgdGApLgoKQXQgdGhlIGVuZCBvZiB0aGlzIGxvb3AsIG91ciBgZGF0YCBvYmplY3Qgd2lsbCBub3cgaGF2ZSB0d28gY29sdW1ucyBhbmQgMTAwIHJvd3MuCgojIyBQbG90dGluZyBpdAoKTm93IHdlIHNpbXBseSBwbG90IGl0LiBTZWUgYGhlbHAobWF0cGxvdClgIGZvciBtb3JlIGluZm9ybWF0aW9uLiBXaGVuIHlvdSBkbyBub3Qgc3VwcGx5IGFuIGB4YCBhcmd1bWVudCB0byBgbWF0cGxvdCgpYCBpdCBzaW1wbHkgcGxvdHMgZWFjaCBjb2x1bW4gb2YgdGhlIG1hdHJpeCBvbiB0aGUgeS1heGlzIHdpdGggZXF1YWwtc3BhY2VkIG51bWJlcnMgb24gdGhlIHgtYXhpcy4gVGhlIGF4aXMgbGFiZWxzIGNhbiBiZSBjaGFuZ2VkIGJ5IHVzaW5nIGB4bGFiYCBhbmQgYHlsYWJgLiBUaGUgdHlwZSBvZiBwbG90IGlzIGEgbGluZSBwbG90IHNvIHdlIHNwZWNpZnkgYGxpbmU9ImwiYC4gQW5kIHdlIHdhbnQgYWxsIHRoZSBsaW5lcyB0byBiZSBhIHNvbGlkIChgMWApIGxpbmUgc28gd2Ugc3BlY2lmeSB0aGUgbGluZXR5cGVzIGJ5IHVzaW5nIGBsdHkgPSAxYC4KClRoZSBgbGVnZW5kKClgIGNvbW1hbmQgYWRkcyBhIGxlZ2VuZCB0byBvdXIgY3VycmVudCBwbG90IGluIHRoZSBgInRvcHJpZ2h0ImAgY29ybmVyLiBXZSBzcGVjaWZ5IHRoZSBjb2xvcnMgYXMgYDE6MmAgcmVmZXJyaW5nIHRvIHRoZSBmaXJzdCBjb2x1bW4gYW5kIHNlY29uZCBjb2x1bW4uIFNpbWlsYXJseSwgdGhlIGxlZ2VuZCB0ZXh0IGlzIGluIHRoZSBzYW1lIG9yZGVyIGFzIHRoZSBjb2x1bW5zLiBUaGUgbGluZSB3aWR0aCBpcyBzZXQgdG8gMSB1c2luZyBgbHdkPTFgLgoKYGBge3J9Cm1hdHBsb3QoZGF0LCB4bGFiPSJ0aW1lIiwgeWxhYj0ibnVtYmVyIG9mIHBlb3BsZSIsIHR5cGU9ImwiLCBsdHkgPSAxKQpsZWdlbmQoInRvcHJpZ2h0IiwgY29sID0gMToyLCBsZWdlbmQgPSBjKCJubyBoZWFkYWNoZSIsImhlYWRhY2hlIiksIGx3ZD0xKQpgYGAKCkl0IGFwcGVhcnMgdmVyeSBsaXR0bGUgaXMgaGFwcGVuaW5nIGluIG91ciBwbG90cyBhZnRlciBhYm91dCB0aW1lIDEwLiBXZSBjYW4gbGltaXQgdGhlIHgtYXhpcyB1c2luZyBgeGxpbWAgaW4gdGhlIGBtYXRwbG90KClgIGNvbW1hbmQuIFRoaXMgb3B0aW9uIHRha2VzIGEgdmVjdG9yIG9mIGxlbmd0aCB0d28gd2l0aCB0aGUgZmlyc3QgYmVpbmcgdGhlIGxvd2VyIGJvdW5kIGFuZCB0aGUgc2Vjb25kIGJlaW5nIHRoZSB1cHBlciBib3VuZC4KCmRhdCA8LSBOVUxMCgojIyBNb2RpZnlpbmcgcGFyYW1ldGVycyBhbmQgdGltZSBzZXF1ZW5jZQpMZXQncyB0cnkgYSBkaWZmZXJlbnQgc2V0IG9mIHBhcmFtZXRlcnMgYW5kIHNlZSBpZiB3ZSBjYW4gZ2V0IHNvbWV0aGluZyBtb3JlIGludGVyZXN0aW5nLgoKYGBge3J9Ck5fdCA8LSAxMDAwMDAgIApQX3QgPC0gMCAKaW5jaWRlbmNlIDwtIDAuMDc1CnJlY292ZXJ5IDwtIDAuMTUgCgp0aW1lc3RlcHMgPC0gMToxMDAgIApgYGAKClRoZW4gbGV0J3MgY2xlYXIgb3V0IHRoZSBgZGF0YCBvYmplY3QgYWdhaW4gYW5kIHJlLXJ1biBvdXIgbG9vcC4KYGBge3J9CmRhdCA8LSBOVUxMCmZvciAodCBpbiB0aW1lc3RlcHMpIHsKICAgIE5fdDEgPC0gTl90IC0gcm91bmQoaW5jaWRlbmNlKk5fdCkgKyByb3VuZChyZWNvdmVyeSpQX3QpIAoKICAgIFBfdDEgPC0gUF90ICsgcm91bmQoaW5jaWRlbmNlKk5fdCkgLSByb3VuZChyZWNvdmVyeSpQX3QpCgogICAgZGF0IDwtIHJiaW5kKGRhdCwgYyhOX3QxLCBQX3QxKSkgCgogICAgTl90IDwtIE5fdDEgCiAgICBQX3QgPC0gUF90MSAKfQpgYGAKCkFuZCBwbG90IHRoZSByZXN1bHRzOgpgYGB7cn0KbWF0cGxvdChkYXQsIHhsYWI9InRpbWUiLCB5bGFiPSJudW1iZXIgb2YgcGVvcGxlIiwgdHlwZT0ibCIsIGx0eSA9IDEpCmxlZ2VuZCgidG9wcmlnaHQiLCBjb2wgPSAxOjIsIGxlZ2VuZCA9IGMoIm5vIGhlYWRhY2hlIiwiaGVhZGFjaGUiKSwgbHdkPTEpCmBgYAoKTm93IGl0IGxvb2tzIGxpa2Ugc29tZXRoaW5nIGlzIGhhcHBlbmluZy4gVHJ5IG90aGVyIHBhcmFtZXRlcnMgb24geW91ciBvd24uCgojIyBBZGRpbmcgYmlydGhzIGFuZCBkZWF0aHMKClN1cHBvc2Ugd2UgYXJlIGludGVyZXN0ZWQgaW4gYWRkaW5nIGJpcnRocyBhbmQgZGVhdGhzIHRvIG91ciBtb2RlbC4gRnVydGhlciwgYXNzdW1lIGV2ZXJ5Ym9keSBjYW4gaGF2ZSBhIGNoaWxkLCBidXQgbm8gY2hpbGRyZW4gYXJlIGJvcm4gd2l0aCBoZWFkYWNoZXMgKG9yIGFmZmVjdGVkKS4gT3VyIG1vZGVsIHdvdWxkIGxvb2sgbGlrZSB0aGlzOgoKJCQKXGJlZ2lue2FsaWduZWR9Ck5fe3QrMX0gJj0gTl90IC0gXGJldGEgTl90ICsgXGdhbW1hIFBfdCArIGJOX3QgKyBiUF90IC0gZE5fdCBcXCAKUF97dCsxfSAmPSBQX3QgLSBcZ2FtbWEgUF90ICsgXGJldGEgTl90IC0gZFBfdApcZW5ke2FsaWduZWR9CiQkCgpIb3cgd291bGQgdGhpcyBsb29rIGluIG91ciBjb2RlPyBGaXJzdCwgd2Ugd291bGQgbmVlZCBzb21lIHZhbHVlcyBmb3IgYGJgIGFuZCBgZGA6CgpgYGB7cn0KYiA8LSAuMDAwMQpkIDwtIC4wMDAyCmBgYAoKTGV0J3MgYWxzbyBtYWtlIG91ciB0aW1lbGluZSBsb25nZXIgc28gd2UgY2FuIHNlZSB0aGUgbG9uZy10ZXJtIGR5bmFtaWNzIG9mIGFkZGluZyBiaXJ0aHMgYW5kIGRlYXRocy4KCmBgYHtyfQp0aW1lc3RlcHMgPC0gMToxMDAwCmBgYAoKClRoZW4gd2Ugd291bGQgbmVlZCB0byB1cGRhdGUgb3VyIGNvbXBhcnRtZW50cyBpbiB0aGUgYGZvcigpYCBsb29wLgpgYGB7cn0KZGF0IDwtIE5VTEwKZm9yICh0IGluIHRpbWVzdGVwcykgewogICAgTl90MSA8LSBOX3QgLSByb3VuZChpbmNpZGVuY2UqTl90KSArIHJvdW5kKHJlY292ZXJ5KlBfdCkgKyBiKk5fdCArIGIqUF90IC0gZCpOX3QKCiAgICBQX3QxIDwtIFBfdCArIHJvdW5kKGluY2lkZW5jZSpOX3QpIC0gcm91bmQocmVjb3ZlcnkqUF90KSAtIGQqUF90CgogICAgZGF0IDwtIHJiaW5kKGRhdCwgYyhOX3QxLCBQX3QxKSkgCgogICAgTl90IDwtIE5fdDEgCiAgICBQX3QgPC0gUF90MSAKfQpgYGAKCkFuZCBwbG90IHRoZSByZXN1bHRzOgpgYGB7cn0KbWF0cGxvdChkYXQsIHhsYWI9InRpbWUiLCB5bGFiPSJudW1iZXIgb2YgcGVvcGxlIiwgdHlwZT0ibCIsIGx0eSA9IDEpCmxlZ2VuZCgidG9wcmlnaHQiLCBjb2wgPSAxOjIsIGxlZ2VuZCA9IGMoIm5vIGhlYWRhY2hlIiwiaGVhZGFjaGUiKSwgbHdkPTEpCmBgYAoKV2Ugc2VlIHRoYXQgd2UgaGl0ICJoZWFkYWNoZSBlcXVpbGlicml1bSIgcmF0aGVyIHF1aWNrbHksIGJ1dCBiZWNhdXNlIG91ciBkZWF0aCByYXRlIGlzIGdyZWF0ZXIgdGhhbiBvdXIgYmlydGggcmF0ZSwgb3VyIHBvcHVsYXRpb24gaXMgc2xvd2x5IGR5aW5nIG9mZi4gVHJ5IG90aGVyIHBhcmFtZXRlcnMgdG8gc2VlIHdoYXQgaGFwcGVucy4K