1 Solution: Simulating competing hazards

The model we want to specify in this etivity has 3 compartments: \(I\) (infected), \(R\) (recovered) and \(M\) (dead). Like in the first etivity, infected people can recover at a rate \(\gamma\). Additionally they can also now die (transition to the \(M\) compartment) at a rate \(\mu\).

The differential equations for this model look like this: \[\begin{align} \frac{dI}{dt} & = -\gamma I -\mu I \\ \frac{dR}{dt} & = \gamma I \\ \frac{dM}{dt} & = \mu I \end{align}\]

This corresponds to the following model diagram:

As you can see, the equation describing the rate of change in the recovered (\(R\)) compartment (second line) is not affected by this addition. However, we need a new equation describing the rate of change in the deceased (\(M\)) compartment (third line). People move into this compartment from the infected compartment (\(I\)) at a rate \(\mu\) - this transition also needs to be added in the rate of change in the infected compartment \(I\) (first line).

The model function describing this set of differential equations looks like this:

# As always, the model function takes as input arguments
# (in the following order): time, state and parameters

cohort_model <- function(time, state, parameters) {  

    with(as.list(c(state, parameters)), {  # tell R to unpack the state
                                           # variable names (I, R and M)
                                           # and model parameters 
                                           # (gamma and mu) from the inputs
                                           # "state" and "parameters"
        
    # The differential equations
      dI <- -gamma * I - mu * I  # included -mu * I
      dR <- gamma * I            # no change
      dM <- mu * I               # a new line to describe the rate of 
                                 # change in the M compartment
      
    # Return the number of people in each compartment at each
    # timestep (in the same order as the input state variables)
    return(list(c(dI, dR, dM))) 
    })
  
}

Defining model input and timesteps

initial_state_values <- c(I = 1000000,   # at the start, there are 10^6 
                                         # infected people
                          R = 0,         # no one has recovered
                          M = 0)         # no one has died 

parameters <- c(gamma = 0.1,       # the recovery rate gamma is 0.1 days^-1
                mu = 0.2)          # the mortality rate mu is 0.2 days^-1

times <- seq(from = 0, to = 4*7, by = 1) # model the course of the
                                         # infection over 4 weeks = 4*7 days

# remember that the rates are in units of days^-1

1.0.1 After 4 weeks, do you expect more people to have recovered or more people to have died, and why?

We expect more people to die than to recover because the mortality rate (0.2) is higher than the recovery rate (0.1), so people move more quickly from \(I\) to \(M\) than from \(I\) to \(R\).

Load the deSolve package and use it to solve the differential equations:

library(deSolve)
library(reshape2)
library(ggplot2)

output <- as.data.frame(ode(y = initial_state_values, 
                            times = times, 
                            func = cohort_model,
                            parms = parameters))

Plotting the output over time indeed shows that more people have indeed died than recovered:

output_long <- melt(as.data.frame(output), id = "time")     

ggplot(data = output_long,   # specify object containing data to plot
       aes(x = time, 
           y = value, 
           colour = variable, 
           group = variable)) +       # assign columns to axes and groups
  geom_line() +                       # represent data as lines
  xlab("Time (days)")+                # add label for x axis
  ylab("Number of people") +          # add label for y axis
  labs(colour = "Compartment")        # add legend title

# ggplot automatically adds the new compartment as a separate colour/group 
# because it is contained within the output dataframe

1.0.2 Based on the model output, what proportion of the initially infected cohort died before recovering over the 4 week period?

# Printing the full model output:
output
##    time            I         R        M
## 1     0 1000000.0000      0.00      0.0
## 2     1  740818.2240  86393.93 172787.9
## 3     2  548811.6238 150396.13 300792.3
## 4     3  406569.6441 197810.12 395620.2
## 5     4  301194.1295 232935.29 465870.6
## 6     5  223130.0332 258956.66 517913.3
## 7     6  165298.7445 278233.75 556467.5
## 8     7  122456.2989 292514.57 585029.1
## 9     8   90717.8284 303094.06 606188.1
## 10    9   67205.4086 310931.53 621863.1
## 11   10   49786.9751 316737.67 633475.3
## 12   11   36883.0919 321038.97 642077.9
## 13   12   27323.6579 324225.45 648450.9
## 14   13   20241.8599 326586.05 653172.1
## 15   14   14995.5341 328334.82 656669.6
## 16   15   11108.9626 329630.35 659260.7
## 17   16    8229.7197 330590.09 661180.2
## 18   17    6096.7248 331301.09 662602.2
## 19   18    4516.5637 331827.81 663655.6
## 20   19    3345.9518 332218.02 664436.0
## 21   20    2478.7415 332507.09 665014.2
## 22   21    1836.2963 332721.23 665442.5
## 23   22    1360.3615 332879.88 665759.8
## 24   23    1007.7803 332997.41 665994.8
## 25   24     746.5819 333084.47 666168.9
## 26   25     553.0813 333148.97 666297.9
## 27   26     409.7326 333196.76 666393.5
## 28   27     303.5373 333232.15 666464.3
## 29   28     224.8659 333258.38 666516.8
# Printing the output at timestep/day 28:
output[output$time == 28,]
##    time        I        R        M
## 29   28 224.8659 333258.4 666516.8
# Divide the number of people who died over the 4 week period by the 
# number of people initially infected:
output[29,"M"]/1000000
## [1] 0.6665168
# Answer: proportion = 0.6665

1.0.3 Now use the competing hazards formula given in the video lecture to calculate the case fatality rate. Does this agree with your answer to the previous question?

The formula is: \[\begin{align} CFR = \frac{\mu}{\mu+\gamma} \end{align}\]

# Calculate this by referring to the values in the parameters vector:
parameters["mu"]/(parameters["mu"]+parameters["gamma"])
##        mu 
## 0.6666667
# Answer: CFR = 0.6667
# This is approximately the same as the case fatality rate
# observed in the model output. The agreement would be exact
# if we allowed the simulation to run for longer.

1.0.4 Which value of \(\mu\) do you need to get a case fatality rate of 50% assuming \(\gamma\) stays fixed?

Rearranging the CFR equation above to solve for \(\mu\) gives:

\[\begin{align} CFR = \frac{\mu}{\mu+\gamma} \\ \mu = CFR(\mu+\gamma) \\ \mu = \mu CFR + \gamma CFR \\ \mu - \mu CFR = \gamma CFR \\ \mu(1-CFR) = \gamma CFR \\ \mu = \frac{\gamma CFR}{1-CFR} \end{align}\]

Calculating \(\mu\) with CFR = 0.50 and \(\gamma\) = 0.1:

\[\begin{align} \mu = \frac{0.1 * 0.50}{1-0.50} \\ \mu = 0.1 \end{align}\]

This makes sense! If \(\mu\) and \(\gamma\) are equal, then they represent two competing hazards that are also equal. Thus, half of people die and half recover, so the case fatality rate is 50%.

Double-check this by modifying the code to simulate the model using these parameters:

# The only thing we are changing is mu, so we only need to update
# the parameters vector and solve the model with this
parameters <- c(gamma = 0.1, mu = 0.1)

output <- as.data.frame(ode(y = initial_state_values, 
                            times = times, 
                            func = cohort_model,
                            parms = parameters))

# Calculate the case fatality rate:
output[29,"M"]/1000000
## [1] 0.4981511
# This indeed gives us approximately 0.50

Again we can plot the output and compare it with what we got with our previous parameters:

output_long <- melt(as.data.frame(output), id = "time")     

ggplot(data = output_long,         # specify object containing data to plot
       aes(x = time, 
           y = value, 
           colour = variable, 
           group = variable)) +         # assign columns to axes and groups
  geom_line() +                         # represent data as lines
  xlab("Time (days)")+                  # add label for x axis
  ylab("Number of people") +            # add label for y axis
  labs(colour = "Compartment")          # add legend title              

As you notice, we only see red and blue lines here representing the number of people in the \(I\) and \(M\) compartment, despite having plotted all 3 compartments. This is just because, with \(\mu\) = \(\gamma\) and \(R(0) = M(0)\) (the initial number recovered and deceased), the number of recovered and deceased people is identical at each timestep so the lines completely overlap.

LS0tDQp0aXRsZTogIlNvbHV0aW9uIDIiDQphdXRob3I6ICJCaW5oIFRoYW5nIFRyYW4iDQpkYXRlOiAiNS8xMC8yMDIwIg0KDQpvdXRwdXQ6DQogIGh0bWxfZG9jdW1lbnQ6DQogICAgY29kZV9kb3dubG9hZDogeWVzDQogICAgY29kZV9mb2xkaW5nOiBoaWRlDQogICAgbnVtYmVyX3NlY3Rpb25zOiB5ZXMNCiAgICB0aGVtZTogam91cm5hbA0KICAgIHRvYzogeWVzDQogICAgdG9jX2Zsb2F0OiB5ZXMNCiAgd29yZF9kb2N1bWVudDoNCiAgICB0b2M6IHllcw0KLS0tDQoNCiMgU29sdXRpb246IFNpbXVsYXRpbmcgY29tcGV0aW5nIGhhemFyZHMNCg0KVGhlIG1vZGVsIHdlIHdhbnQgdG8gc3BlY2lmeSBpbiB0aGlzIGV0aXZpdHkgaGFzIDMgY29tcGFydG1lbnRzOiAkSSQgKGluZmVjdGVkKSwgJFIkIChyZWNvdmVyZWQpIGFuZCAkTSQgKGRlYWQpLiBMaWtlIGluIHRoZSBmaXJzdCBldGl2aXR5LCBpbmZlY3RlZCBwZW9wbGUgY2FuIHJlY292ZXIgYXQgYSByYXRlICRcZ2FtbWEkLiBBZGRpdGlvbmFsbHkgdGhleSBjYW4gYWxzbyBub3cgZGllICh0cmFuc2l0aW9uIHRvIHRoZSAkTSQgY29tcGFydG1lbnQpIGF0IGEgcmF0ZSAkXG11JC4NCg0KVGhlIGRpZmZlcmVudGlhbCBlcXVhdGlvbnMgZm9yIHRoaXMgbW9kZWwgbG9vayBsaWtlIHRoaXM6DQpcYmVnaW57YWxpZ259DQpcZnJhY3tkSX17ZHR9ICYgPSAtXGdhbW1hIEkgLVxtdSBJIFxcDQpcZnJhY3tkUn17ZHR9ICYgPSBcZ2FtbWEgSSBcXA0KXGZyYWN7ZE19e2R0fSAmID0gXG11IEkNClxlbmR7YWxpZ259DQoNClRoaXMgY29ycmVzcG9uZHMgdG8gdGhlIGZvbGxvd2luZyBtb2RlbCBkaWFncmFtOg0KDQoNCg0KQXMgeW91IGNhbiBzZWUsIHRoZSBlcXVhdGlvbiBkZXNjcmliaW5nIHRoZSByYXRlIG9mIGNoYW5nZSBpbiB0aGUgcmVjb3ZlcmVkICgkUiQpIGNvbXBhcnRtZW50IChzZWNvbmQgbGluZSkgaXMgbm90IGFmZmVjdGVkIGJ5IHRoaXMgYWRkaXRpb24uIEhvd2V2ZXIsIHdlIG5lZWQgYSBuZXcgZXF1YXRpb24gZGVzY3JpYmluZyB0aGUgcmF0ZSBvZiBjaGFuZ2UgaW4gdGhlIGRlY2Vhc2VkICgkTSQpIGNvbXBhcnRtZW50ICh0aGlyZCBsaW5lKS4gUGVvcGxlIG1vdmUgaW50byB0aGlzIGNvbXBhcnRtZW50IGZyb20gdGhlIGluZmVjdGVkIGNvbXBhcnRtZW50ICgkSSQpIGF0IGEgcmF0ZSAkXG11JCAtIHRoaXMgdHJhbnNpdGlvbiBhbHNvIG5lZWRzIHRvIGJlIGFkZGVkIGluIHRoZSByYXRlIG9mIGNoYW5nZSBpbiB0aGUgaW5mZWN0ZWQgY29tcGFydG1lbnQgJEkkIChmaXJzdCBsaW5lKS4gDQoNClRoZSAqKm1vZGVsIGZ1bmN0aW9uKiogZGVzY3JpYmluZyB0aGlzIHNldCBvZiBkaWZmZXJlbnRpYWwgZXF1YXRpb25zIGxvb2tzIGxpa2UgdGhpczoNCg0KYGBge3J9DQojIEFzIGFsd2F5cywgdGhlIG1vZGVsIGZ1bmN0aW9uIHRha2VzIGFzIGlucHV0IGFyZ3VtZW50cw0KIyAoaW4gdGhlIGZvbGxvd2luZyBvcmRlcik6IHRpbWUsIHN0YXRlIGFuZCBwYXJhbWV0ZXJzDQoNCmNvaG9ydF9tb2RlbCA8LSBmdW5jdGlvbih0aW1lLCBzdGF0ZSwgcGFyYW1ldGVycykgeyAgDQoNCiAgICB3aXRoKGFzLmxpc3QoYyhzdGF0ZSwgcGFyYW1ldGVycykpLCB7ICAjIHRlbGwgUiB0byB1bnBhY2sgdGhlIHN0YXRlDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyB2YXJpYWJsZSBuYW1lcyAoSSwgUiBhbmQgTSkNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIGFuZCBtb2RlbCBwYXJhbWV0ZXJzIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgKGdhbW1hIGFuZCBtdSkgZnJvbSB0aGUgaW5wdXRzDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyAic3RhdGUiIGFuZCAicGFyYW1ldGVycyINCiAgICAgICAgDQogICAgIyBUaGUgZGlmZmVyZW50aWFsIGVxdWF0aW9ucw0KICAgICAgZEkgPC0gLWdhbW1hICogSSAtIG11ICogSSAgIyBpbmNsdWRlZCAtbXUgKiBJDQogICAgICBkUiA8LSBnYW1tYSAqIEkgICAgICAgICAgICAjIG5vIGNoYW5nZQ0KICAgICAgZE0gPC0gbXUgKiBJICAgICAgICAgICAgICAgIyBhIG5ldyBsaW5lIHRvIGRlc2NyaWJlIHRoZSByYXRlIG9mIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBjaGFuZ2UgaW4gdGhlIE0gY29tcGFydG1lbnQNCiAgICAgIA0KICAgICMgUmV0dXJuIHRoZSBudW1iZXIgb2YgcGVvcGxlIGluIGVhY2ggY29tcGFydG1lbnQgYXQgZWFjaA0KICAgICMgdGltZXN0ZXAgKGluIHRoZSBzYW1lIG9yZGVyIGFzIHRoZSBpbnB1dCBzdGF0ZSB2YXJpYWJsZXMpDQogICAgcmV0dXJuKGxpc3QoYyhkSSwgZFIsIGRNKSkpIA0KICAgIH0pDQogIA0KfQ0KYGBgDQoNCg0KDQoqKkRlZmluaW5nIG1vZGVsIGlucHV0IGFuZCB0aW1lc3RlcHMqKg0KDQoNCmBgYHtSfQ0KaW5pdGlhbF9zdGF0ZV92YWx1ZXMgPC0gYyhJID0gMTAwMDAwMCwgICAjIGF0IHRoZSBzdGFydCwgdGhlcmUgYXJlIDEwXjYgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgaW5mZWN0ZWQgcGVvcGxlDQogICAgICAgICAgICAgICAgICAgICAgICAgIFIgPSAwLCAgICAgICAgICMgbm8gb25lIGhhcyByZWNvdmVyZWQNCiAgICAgICAgICAgICAgICAgICAgICAgICAgTSA9IDApICAgICAgICAgIyBubyBvbmUgaGFzIGRpZWQgDQoNCnBhcmFtZXRlcnMgPC0gYyhnYW1tYSA9IDAuMSwgICAgICAgIyB0aGUgcmVjb3ZlcnkgcmF0ZSBnYW1tYSBpcyAwLjEgZGF5c14tMQ0KICAgICAgICAgICAgICAgIG11ID0gMC4yKSAgICAgICAgICAjIHRoZSBtb3J0YWxpdHkgcmF0ZSBtdSBpcyAwLjIgZGF5c14tMQ0KDQp0aW1lcyA8LSBzZXEoZnJvbSA9IDAsIHRvID0gNCo3LCBieSA9IDEpICMgbW9kZWwgdGhlIGNvdXJzZSBvZiB0aGUNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBpbmZlY3Rpb24gb3ZlciA0IHdlZWtzID0gNCo3IGRheXMNCg0KIyByZW1lbWJlciB0aGF0IHRoZSByYXRlcyBhcmUgaW4gdW5pdHMgb2YgZGF5c14tMQ0KYGBgDQoNCiMjIyBBZnRlciA0IHdlZWtzLCBkbyB5b3UgZXhwZWN0IG1vcmUgcGVvcGxlIHRvIGhhdmUgcmVjb3ZlcmVkIG9yIG1vcmUgcGVvcGxlIHRvIGhhdmUgZGllZCwgYW5kIHdoeT8gDQoNCldlIGV4cGVjdCBtb3JlIHBlb3BsZSB0byBkaWUgdGhhbiB0byByZWNvdmVyIGJlY2F1c2UgdGhlIG1vcnRhbGl0eSByYXRlICgwLjIpIGlzIGhpZ2hlciB0aGFuIHRoZSByZWNvdmVyeSByYXRlICgwLjEpLCBzbyBwZW9wbGUgbW92ZSBtb3JlIHF1aWNrbHkgZnJvbSAkSSQgdG8gJE0kIHRoYW4gZnJvbSAkSSQgdG8gJFIkLg0KDQpMb2FkIHRoZSBkZVNvbHZlIHBhY2thZ2UgYW5kIHVzZSBpdCB0byBzb2x2ZSB0aGUgZGlmZmVyZW50aWFsIGVxdWF0aW9uczoNCg0KDQpgYGB7Un0NCmxpYnJhcnkoZGVTb2x2ZSkNCmxpYnJhcnkocmVzaGFwZTIpDQpsaWJyYXJ5KGdncGxvdDIpDQoNCm91dHB1dCA8LSBhcy5kYXRhLmZyYW1lKG9kZSh5ID0gaW5pdGlhbF9zdGF0ZV92YWx1ZXMsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRpbWVzID0gdGltZXMsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZ1bmMgPSBjb2hvcnRfbW9kZWwsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgcGFybXMgPSBwYXJhbWV0ZXJzKSkNCmBgYA0KDQpQbG90dGluZyB0aGUgb3V0cHV0IG92ZXIgdGltZSBpbmRlZWQgc2hvd3MgdGhhdCBtb3JlIHBlb3BsZSBoYXZlIGluZGVlZCBkaWVkIHRoYW4gcmVjb3ZlcmVkOg0KDQoNCmBgYHtSfQ0Kb3V0cHV0X2xvbmcgPC0gbWVsdChhcy5kYXRhLmZyYW1lKG91dHB1dCksIGlkID0gInRpbWUiKSAgICAgDQoNCmdncGxvdChkYXRhID0gb3V0cHV0X2xvbmcsICAgIyBzcGVjaWZ5IG9iamVjdCBjb250YWluaW5nIGRhdGEgdG8gcGxvdA0KICAgICAgIGFlcyh4ID0gdGltZSwgDQogICAgICAgICAgIHkgPSB2YWx1ZSwgDQogICAgICAgICAgIGNvbG91ciA9IHZhcmlhYmxlLCANCiAgICAgICAgICAgZ3JvdXAgPSB2YXJpYWJsZSkpICsgICAgICAgIyBhc3NpZ24gY29sdW1ucyB0byBheGVzIGFuZCBncm91cHMNCiAgZ2VvbV9saW5lKCkgKyAgICAgICAgICAgICAgICAgICAgICAgIyByZXByZXNlbnQgZGF0YSBhcyBsaW5lcw0KICB4bGFiKCJUaW1lIChkYXlzKSIpKyAgICAgICAgICAgICAgICAjIGFkZCBsYWJlbCBmb3IgeCBheGlzDQogIHlsYWIoIk51bWJlciBvZiBwZW9wbGUiKSArICAgICAgICAgICMgYWRkIGxhYmVsIGZvciB5IGF4aXMNCiAgbGFicyhjb2xvdXIgPSAiQ29tcGFydG1lbnQiKSAgICAgICAgIyBhZGQgbGVnZW5kIHRpdGxlDQoNCiMgZ2dwbG90IGF1dG9tYXRpY2FsbHkgYWRkcyB0aGUgbmV3IGNvbXBhcnRtZW50IGFzIGEgc2VwYXJhdGUgY29sb3VyL2dyb3VwIA0KIyBiZWNhdXNlIGl0IGlzIGNvbnRhaW5lZCB3aXRoaW4gdGhlIG91dHB1dCBkYXRhZnJhbWUNCmBgYA0KDQojIyMgQmFzZWQgb24gdGhlIG1vZGVsIG91dHB1dCwgd2hhdCBwcm9wb3J0aW9uIG9mIHRoZSBpbml0aWFsbHkgaW5mZWN0ZWQgY29ob3J0IGRpZWQgYmVmb3JlIHJlY292ZXJpbmcgb3ZlciB0aGUgNCB3ZWVrIHBlcmlvZD8NCg0KDQpgYGB7Un0NCiMgUHJpbnRpbmcgdGhlIGZ1bGwgbW9kZWwgb3V0cHV0Og0Kb3V0cHV0DQpgYGANCg0KDQpgYGB7Un0NCiMgUHJpbnRpbmcgdGhlIG91dHB1dCBhdCB0aW1lc3RlcC9kYXkgMjg6DQpvdXRwdXRbb3V0cHV0JHRpbWUgPT0gMjgsXQ0KDQojIERpdmlkZSB0aGUgbnVtYmVyIG9mIHBlb3BsZSB3aG8gZGllZCBvdmVyIHRoZSA0IHdlZWsgcGVyaW9kIGJ5IHRoZSANCiMgbnVtYmVyIG9mIHBlb3BsZSBpbml0aWFsbHkgaW5mZWN0ZWQ6DQpvdXRwdXRbMjksIk0iXS8xMDAwMDAwDQojIEFuc3dlcjogcHJvcG9ydGlvbiA9IDAuNjY2NQ0KYGBgDQoNCiMjIyBOb3cgdXNlIHRoZSBjb21wZXRpbmcgaGF6YXJkcyBmb3JtdWxhIGdpdmVuIGluIHRoZSB2aWRlbyBsZWN0dXJlIHRvIGNhbGN1bGF0ZSB0aGUgY2FzZSBmYXRhbGl0eSByYXRlLiBEb2VzIHRoaXMgYWdyZWUgd2l0aCB5b3VyIGFuc3dlciB0byB0aGUgcHJldmlvdXMgcXVlc3Rpb24/DQoNClRoZSBmb3JtdWxhIGlzOiANClxiZWdpbnthbGlnbn0NCkNGUiA9IFxmcmFje1xtdX17XG11K1xnYW1tYX0gDQpcZW5ke2FsaWdufQ0KDQoNCmBgYHtSfQ0KIyBDYWxjdWxhdGUgdGhpcyBieSByZWZlcnJpbmcgdG8gdGhlIHZhbHVlcyBpbiB0aGUgcGFyYW1ldGVycyB2ZWN0b3I6DQpwYXJhbWV0ZXJzWyJtdSJdLyhwYXJhbWV0ZXJzWyJtdSJdK3BhcmFtZXRlcnNbImdhbW1hIl0pDQojIEFuc3dlcjogQ0ZSID0gMC42NjY3DQojIFRoaXMgaXMgYXBwcm94aW1hdGVseSB0aGUgc2FtZSBhcyB0aGUgY2FzZSBmYXRhbGl0eSByYXRlDQojIG9ic2VydmVkIGluIHRoZSBtb2RlbCBvdXRwdXQuIFRoZSBhZ3JlZW1lbnQgd291bGQgYmUgZXhhY3QNCiMgaWYgd2UgYWxsb3dlZCB0aGUgc2ltdWxhdGlvbiB0byBydW4gZm9yIGxvbmdlci4NCmBgYA0KDQojIyMgV2hpY2ggdmFsdWUgb2YgJFxtdSQgZG8geW91IG5lZWQgdG8gZ2V0IGEgY2FzZSBmYXRhbGl0eSByYXRlIG9mIDUwJSBhc3N1bWluZyAkXGdhbW1hJCBzdGF5cyBmaXhlZD8gDQoNClJlYXJyYW5naW5nIHRoZSBDRlIgZXF1YXRpb24gYWJvdmUgdG8gc29sdmUgZm9yICRcbXUkIGdpdmVzOg0KDQpcYmVnaW57YWxpZ259DQpDRlIgPSBcZnJhY3tcbXV9e1xtdStcZ2FtbWF9IFxcDQpcbXUgPSBDRlIoXG11K1xnYW1tYSkgXFwNClxtdSA9IFxtdSBDRlIgKyBcZ2FtbWEgQ0ZSIFxcDQpcbXUgLSBcbXUgQ0ZSID0gXGdhbW1hIENGUiBcXA0KXG11KDEtQ0ZSKSA9IFxnYW1tYSBDRlIgXFwNClxtdSA9IFxmcmFje1xnYW1tYSBDRlJ9ezEtQ0ZSfQ0KXGVuZHthbGlnbn0NCg0KQ2FsY3VsYXRpbmcgJFxtdSQgd2l0aCBDRlIgPSAwLjUwIGFuZCAkXGdhbW1hJCA9IDAuMToNCg0KXGJlZ2lue2FsaWdufQ0KXG11ID0gXGZyYWN7MC4xICogMC41MH17MS0wLjUwfSBcXA0KXG11ID0gMC4xDQpcZW5ke2FsaWdufQ0KDQpUaGlzIG1ha2VzIHNlbnNlISBJZiAkXG11JCBhbmQgJFxnYW1tYSQgYXJlIGVxdWFsLCB0aGVuIHRoZXkgcmVwcmVzZW50IHR3byBjb21wZXRpbmcgaGF6YXJkcyB0aGF0IGFyZSBhbHNvIGVxdWFsLiBUaHVzLCBoYWxmIG9mIHBlb3BsZSBkaWUgYW5kIGhhbGYgcmVjb3Zlciwgc28gdGhlIGNhc2UgZmF0YWxpdHkgcmF0ZSBpcyA1MCUuDQoNCkRvdWJsZS1jaGVjayB0aGlzIGJ5IG1vZGlmeWluZyB0aGUgY29kZSB0byBzaW11bGF0ZSB0aGUgbW9kZWwgdXNpbmcgdGhlc2UgcGFyYW1ldGVyczoNCg0KDQpgYGB7Un0NCiMgVGhlIG9ubHkgdGhpbmcgd2UgYXJlIGNoYW5naW5nIGlzIG11LCBzbyB3ZSBvbmx5IG5lZWQgdG8gdXBkYXRlDQojIHRoZSBwYXJhbWV0ZXJzIHZlY3RvciBhbmQgc29sdmUgdGhlIG1vZGVsIHdpdGggdGhpcw0KcGFyYW1ldGVycyA8LSBjKGdhbW1hID0gMC4xLCBtdSA9IDAuMSkNCg0Kb3V0cHV0IDwtIGFzLmRhdGEuZnJhbWUob2RlKHkgPSBpbml0aWFsX3N0YXRlX3ZhbHVlcywgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgdGltZXMgPSB0aW1lcywgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgZnVuYyA9IGNvaG9ydF9tb2RlbCwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBwYXJtcyA9IHBhcmFtZXRlcnMpKQ0KDQojIENhbGN1bGF0ZSB0aGUgY2FzZSBmYXRhbGl0eSByYXRlOg0Kb3V0cHV0WzI5LCJNIl0vMTAwMDAwMA0KDQojIFRoaXMgaW5kZWVkIGdpdmVzIHVzIGFwcHJveGltYXRlbHkgMC41MA0KYGBgDQoNCkFnYWluIHdlIGNhbiBwbG90IHRoZSBvdXRwdXQgYW5kIGNvbXBhcmUgaXQgd2l0aCB3aGF0IHdlIGdvdCB3aXRoIG91ciBwcmV2aW91cyBwYXJhbWV0ZXJzOg0KDQoNCmBgYHtSfQ0Kb3V0cHV0X2xvbmcgPC0gbWVsdChhcy5kYXRhLmZyYW1lKG91dHB1dCksIGlkID0gInRpbWUiKSAgICAgDQoNCmdncGxvdChkYXRhID0gb3V0cHV0X2xvbmcsICAgICAgICAgIyBzcGVjaWZ5IG9iamVjdCBjb250YWluaW5nIGRhdGEgdG8gcGxvdA0KICAgICAgIGFlcyh4ID0gdGltZSwgDQogICAgICAgICAgIHkgPSB2YWx1ZSwgDQogICAgICAgICAgIGNvbG91ciA9IHZhcmlhYmxlLCANCiAgICAgICAgICAgZ3JvdXAgPSB2YXJpYWJsZSkpICsgICAgICAgICAjIGFzc2lnbiBjb2x1bW5zIHRvIGF4ZXMgYW5kIGdyb3Vwcw0KICBnZW9tX2xpbmUoKSArICAgICAgICAgICAgICAgICAgICAgICAgICMgcmVwcmVzZW50IGRhdGEgYXMgbGluZXMNCiAgeGxhYigiVGltZSAoZGF5cykiKSsgICAgICAgICAgICAgICAgICAjIGFkZCBsYWJlbCBmb3IgeCBheGlzDQogIHlsYWIoIk51bWJlciBvZiBwZW9wbGUiKSArICAgICAgICAgICAgIyBhZGQgbGFiZWwgZm9yIHkgYXhpcw0KICBsYWJzKGNvbG91ciA9ICJDb21wYXJ0bWVudCIpICAgICAgICAgICMgYWRkIGxlZ2VuZCB0aXRsZSAgICAgICAgICAgICAgDQpgYGANCg0KQXMgeW91IG5vdGljZSwgd2Ugb25seSBzZWUgcmVkIGFuZCBibHVlIGxpbmVzIGhlcmUgcmVwcmVzZW50aW5nIHRoZSBudW1iZXIgb2YgcGVvcGxlIGluIHRoZSAkSSQgYW5kICRNJCBjb21wYXJ0bWVudCwgZGVzcGl0ZSBoYXZpbmcgcGxvdHRlZCBhbGwgMyBjb21wYXJ0bWVudHMuIFRoaXMgaXMganVzdCBiZWNhdXNlLCB3aXRoICRcbXUkID0gJFxnYW1tYSQgYW5kICRSKDApID0gTSgwKSQgKHRoZSBpbml0aWFsIG51bWJlciByZWNvdmVyZWQgYW5kIGRlY2Vhc2VkKSwgdGhlIG51bWJlciBvZiByZWNvdmVyZWQgYW5kIGRlY2Vhc2VkIHBlb3BsZSBpcyBpZGVudGljYWwgYXQgZWFjaCB0aW1lc3RlcCBzbyB0aGUgbGluZXMgY29tcGxldGVseSBvdmVybGFwLg0KDQoNCmBgYHtSfQ0KDQpgYGANCg==