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.

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.

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.

  1. So starting inside, c(N_t1, P_t1) will take the results from the current time step and create a two-item vector.
  2. The rbind() command simply performs glues two vectors together, by row. So dat will be on top, the c(N_t1, P_t1) will be glued beneath it as a new row.
  3. 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