Here is the link to get the code from github https://github.com/chamyields/mastats/tree/main/pinstats_2020

Questions

Consider the paper of David Spiegelhalter (available on Ufora). In this paper the author examines the age-specific risk of dying (with or from) Covid-19 in the UK. Table 2 in the paper, gives for several age categories the numbers of reported Covid-19 deaths, numbers of non-covid related deaths, population sizes, among other numbers.

1. Question

Assign to each age interval a numerical value given by the midpoint (e.g. for 0-4, the midpoint is (0+4)/2 =2) and let \(p_i\) denote the corresponding population size in the UK. Assume that the number of non-covid deaths \((Y^N )\) within an age class with midpoint \(a_i\) \((i = 1,\ldots , 11)\) can be described by a Poisson distribution with mean given by

\[ \text{E}(Y^N|\text{age}=a_i) = p_i\exp(\beta_0^N+\beta_1^Na_i) \]

(the superscript refers to non-covid). Also for the covid-related deaths \((Y^C)\) we assume a Poisson distribution with

\[ \text{E}(Y^C|\text{age}=a_i) = p_i\exp(\beta_0^C+\gamma\beta_1^Na_i) \]

How do you interpret the \(\gamma\) parameter?

Solution

Besides the first and the last age intervals, the difference between the mid point from one age group to the other is 10 years.

  • Lets take a unit increase in age as 10 years. Then \[ \beta_1^N = \log \text{E}(Y^N|\text{age} = a_i+1) - \log \text{E}(Y^N|\text{age} = a_i) \] and \[ \gamma\beta_1^N = \log \text{E}(Y^C|\text{age} = a_i+1) - \log \text{E}(Y^C|\text{age} = a_i) \]
  • \(\beta_1^N\) is the expected change in the number of non-covid-19 deaths per unit increase in age in the logarithmic scale
  • \(\gamma \beta_1^N\) is the expected change in the number of covid-19 deaths per unit increase in age in the logarithmic scale

  • \(\gamma = \gamma\beta_1^N/\beta_1^N\) is ratio of the expected change in the number of covid-19 deaths on the expected change in the number of non-covid 19 deaths for a unit increase in age in the logarithmic scale
  • \(\gamma>0\) implies for a unit increase in age the expected change in the number of covid-19 deaths is greater than that of non-covid 19 deaths.
  • \(\gamma<0\) implies for a unit increase in age, the expected change in the number of covid-19 deaths is less than that of non-covid 19 deaths.
  • \(\gamma = 0\) implies for a unit increase in age, the expected change in the number of covid-19 deaths is equal to that of non-covid 19 deaths. Hence dying from covid 19 compared to dying from a non-covid 19 cause is independent of age.

2. Question

Given the models from the previous question, and assuming that all num- bers of deaths (both covid and non-covid) are independently distributed. Construct the log-likelihood function and make a graph of the log-likelihood as a function of the \(\gamma\) parameter. You may use the following estimates for the other parameters:

\[ \beta_0^N = −12.06 ~~ \beta_0^C = −14.15 ~~ \beta_1^N = 0.09836 \]

Solution

We have been told that \(Y^N\) and \(Y^C\) follow a Poisson distribution, recall that if a random variable \(K\) follows a Poission distribution, it has the follow mass function

\[ P_\lambda(K=k) = f(k|\lambda) = \frac{\lambda^{k}e^{-\lambda}}{k!} \] Let’s say we have observed \(n\) values from \(n\) random variables \(K_1,\ldots,K_n\) that are:

  • all identically distributed (i.e. they all follow a Poisson distribution with parameter \(\lambda_i\), well not exactly identical since their mean \(\lambda\)s are different)
  • independent, i.e, \(P(K_1,\ldots,K_n) = \prod_i P(K_i)\)

The likelihood is then defined as a function of \(\mathbf \Lambda = (\lambda_1,\ldots,\lambda_n)\) given the data, i.e., \[ L(\mathbf \Lambda|K_1=k_1,\ldots,K_n=k_n) = \prod_i P_{\lambda_i}(K_i=k_i) = \prod_if(k_i|\lambda_i) = \prod_i\frac{\lambda_i^{k_i}e^{-\lambda_i}}{k_i!} \] The log likelihood which is simpler transformation to work with is given by \[ l(\Lambda|K_1=k_1,\ldots,K_n=k_n) = \log \prod_i P_{\lambda_i}(K_i=k_i) = \sum_i\log\frac{\lambda_i^{k_i}e^{-\lambda_i}}{k_i!} \]

\[ l(\Lambda|K_1=k_1,\ldots,K_n=k_n) = \sum_i \big(k_i \log\lambda_i - \lambda_i - \log k_i!\big) \]

\[ l(\Lambda|K_1=k_1,\ldots,K_n=k_n) = \sum_i k_i \log\lambda_i - \sum_i \lambda_i - \sum_i \log k_i! \]

For the problem at hand we have:

  • \(\lambda_i^N = \text{E}(Y^N|\text{age}=a_i)\) for non-covid 19 deads
  • \(\lambda_i^C (\gamma) = \text{E}(Y^C|\text{age}=a_i)\) for covid 19 deads
  • our parameter of interest is \(\gamma\).
  • samples size \(n_N=11\) for the covid-19 deads
  • samples size \(n_C=11\) for the non-covid-19 deads

\[ l(\gamma|Y^C_1=Y^C_1,\ldots,Y^C_n=Y^C_n,Y^N_1=y^N_1,\ldots,Y^N_n=y^N_n) = \log \Pi_if(Y^C|\lambda_i^C(\gamma))f(y_i^N|\lambda_i^N) \] \[ l(\gamma|Y^C_1=Y^C_1,\ldots,Y^C_n=Y^C_n,Y^N_1=y^N_1,\ldots,Y^N_n=y^N_n) = \sum_{i=1}^{11}\log f(Y^C|\lambda_i^C(\gamma))+\sum_{i=1}^{11}\log f(y_i^N|\lambda_i^N) \] \[ l(\gamma|Y^C_1=Y^C_1,\ldots,Y^C_n=Y^C_n,Y^N_1=y^N_1,\ldots,Y^N_n=y^N_n) = \sum_{i=1}^{11}\big(Y^C\log \lambda_i^C(\gamma) + y_i^N\log \lambda_i^N\big) -\sum_{i=1}^{11}\big(\lambda_i^C(\gamma)+\lambda_i^N\big)-\sum_{i=1}^{11}\log (Y^Cy_i^N) \] We can simply write our log likelihood function as

\[ l(\gamma) = \sum_{i=1}^{11}\big(Y^C\log \lambda_i^C(\gamma) + y_i^N\log \lambda_i^N\big) -\sum_{i=1}^{11}\big(\lambda_i^C(\gamma)+\lambda_i^N\big)-\sum_{i=1}^{11}\log (Y^CY^N) \] So we have our log likelihood function lets code it in R.

library(dplyr,
        verbose=FALSE)

Attaching package: ‘dplyr’

The following objects are masked from ‘package:stats’:

    filter, lag

The following objects are masked from ‘package:base’:

    intersect, setdiff, setequal, union
library(tidyverse,
        verbose = FALSE)
Registered S3 methods overwritten by 'dbplyr':
  method         from
  print.tbl_lazy     
  print.tbl_sql      
── Attaching packages ────────────────────────────────────────────────────────────────────────────────────── tidyverse 1.3.0 ──
✔ ggplot2 3.2.1     ✔ purrr   0.3.3
✔ tibble  2.1.3     ✔ stringr 1.4.0
✔ tidyr   1.0.2     ✔ forcats 0.5.0
✔ readr   1.3.1     
── Conflicts ───────────────────────────────────────────────────────────────────────────────────────── tidyverse_conflicts() ──
✖ dplyr::filter() masks stats::filter()
✖ dplyr::lag()    masks stats::lag()
age_mid_point = function(x,y){
  (x+y)/2
}

beta_0_C_hat = -14.15
beta_1_N_hat = 0.09836
beta_0_N_hat = -12.06

lb = c(0,5,15,25,35,45,55,65,75,85)
ub = c(4,14,24,34,44,54,64,74,84,89)

age = c(age_mid_point(lb,ub),90)

Y_C = c(3,3,33,128,369,1283,3476,7319,16043,10160,10790)

population = c(3535430,7159102,6988755,
               7998302,7460856,8142528,
               7019590,5906928,3476922,
               918437,528959)

Y_N = c(848,138,470,1141,2577,6577,13565,27184,47729,30703,30703)

data = data.frame(age,Y_N,Y_C,population)


beta_0_C = beta_0_C_hat
beta_0_N = beta_0_N_hat
beta_1_N = beta_1_N_hat


log_l = function(gamma_hat){

  lambda_N = data$population*exp(beta_0_N+beta_1_N*data$age)
  lambda_C = data$population*exp(beta_0_C+gamma_hat*beta_1_N*data$age)
  
  sum_1 = data$Y_C*log(lambda_C)+data$Y_N*log(lambda_N)
  sum_2 = lambda_C+lambda_N
  sum_3 = log(data$Y_C*data$Y_N)
  
  sum_all= sum(sum_1-sum_2-sum_3)
  return(-sum_all)
}
data
log_ls = c()
gammas = seq(1,1.2,length.out = 1000)
for(gamma in gammas){
  log_ls  = c(log_l(gamma),log_ls)
}

log_ls = log_ls/max(abs(log_ls))

plot(x=gammas,y=-log_ls,type="l")
abline(v=gammas[-log_ls==max(-log_ls)])

paste0('optimal gamma: ',round(gammas[-log_ls == max(-log_ls)],3))
[1] "optimal gamma: 1.072"

3.Question

Use the optim (or similar) function in R for finding the maxi- mum likelihood estimate of \(\gamma\).

Solution

Lets look for \(\gamma\) with optim

# initial value of gamma
par = 0

opt = optim(par=par,
      fn= log_l)
one-dimensional optimization by Nelder-Mead is unreliable:
use "Brent" or optimize() directly
opt
$par
[1] 1.127734

$value
[1] -1888745

$counts
function gradient 
      32       NA 

$convergence
[1] 0

$message
NULL

Can you compare the results of the manual computation with that of optim. They seem to be different? Please check if there are any mistakes in my computations

plot(x=gammas,y=-log_ls,type="l")
abline(v=gammas[-log_ls==max(-log_ls)],col='blue')
abline(v=opt$par,col='red')

  • Note that our log_l function returns the negative log likelihood instead because optim is a minimisation function.

  • I checked the paper for the maximum likelihood estimator of \(\gamma\), they also have \((1.128)\), the value from the optim function. So emm…. let’s check the optimise function ;)

optimize(log_l,c(-2,2))
$minimum
[1] 1.127681

$objective
[1] -1888745

4. Question

with the information in Table 2 and with the models and their parameter estimates of the previous questions, estimate the following two probabilities: \[ \text{P} (\text{Age} = 19.5 | \text{ died of covid}) \text{ and } \text{P} (\text{Age} = 79.5 | \text{ died of covid}) \]

Solution

Bayes theorem: for two random variables \(X\) and \(Y\), \[ P(X|Y) = \frac{P(Y|X)P(X)}{P(Y)} \]

Hence

\[ \text{P} (\text{Age} = 19.5 | \text{ died of covid}) = \frac{\text{P} ( \text{ died of covid} | \text{Age} = 19.5 ) \text{P} (\text{Age} = 19.5)}{\text{P}( \text{ died of covid})} \]

  • further, use these values and plug into the formula above.

\[ \text{P} ( \text{ died of covid} | \text{Age} = 19.5 ) = \frac{\text{E}(Y^C|\text{Age} = 19.5)}{\text{population size with mid point at age 19.5}} \] \[ \text{P} (\text{Age} = 19.5) = \frac{\text{Population size with mid point at 19.5}}{\text{Total population size across all age groups}} \]

\[ \text{P} (\text{ died of covid}) = \frac{\text{Total number of people who died from covid across all age groups}}{\text{Total population size across all age groups}} \]

Fill the values and compute this probability, do the same for \(\text{P} (\text{Age} = 79.5 | \text{ died of covid})\)

Question 5.

For this question you may assume that the numerical values for (the estimates) \(\beta_0^C\), \(\beta^N_0\) and \(\beta_1^N\) that were given in question 2, are the true population parameter values.Set up a simulation study in which you simulate data (as in Table 2) for the covid and non-covid related deaths, and calculate the maximum likelihood estimate of \(\gamma\) for each simulated dataset.

Solution

n=11
gamma_hat = 1.2

lambda_N = data$population*exp(beta_0_N+beta_1_N*data$age)
lambda_C = data$population*exp(beta_0_C+gamma_hat*beta_1_N*data$age)

max_lik_est = c()
num_simulations = 1000

for(k in 1:num_simulations){
  
  # set.seed(k)
  
  Y_C = rpois(n,lambda_C)
  Y_N = rpois(n,lambda_N)
  data = data.frame(age,Y_N,Y_C,population)
  
  par = 1
  
  opt = optimize(log_l,c(-1.5,1.5));
  
  max_lik_est[k] = opt$minimum
}
m<-mean(max_lik_est)
std<-sqrt(var(max_lik_est))

hist(max_lik_est, 
     col = 'skyblue3',
     breaks = 20,
     prob=TRUE)
curve(dnorm(x, mean=m, sd=std), 
      col="red", 
      lwd=3, 
      add=TRUE, 
      yaxt="n")

print('')

Assessing normality

qqnorm(max_lik_est, pch = 1, frame = FALSE)
qqline(max_lik_est, col = "steelblue", lwd = 2)

Ok it seems like there are two sets of maximum likelihood estimates:One that occurs at the upper bound of the interval of possible \(\gamma\) values and the others. The upper bound of the interval is taken as the maximum likelihood estimate only when it can not be computed. We will take the upper bound values out of the distribution and reassess.

### taking out max_lik that are greater 1.4
sub_max_lik = max_lik_est[max_lik_est<1.4]

m<-mean(sub_max_lik)
std<-sqrt(var(sub_max_lik))

hist(sub_max_lik, 
     col= 'skyblue3',
     breaks = 20,
     prob=TRUE)

curve(dnorm(x, mean=m, sd=std), 
      col="red", lwd=3, add=TRUE, yaxt="n")
abline(v=mean(sub_max_lik),col="pink",lwd=3)
abline(v=gamma_hat,col="green",lwd=3)
qqnorm(sub_max_lik, pch = 1, frame = FALSE)
qqline(sub_max_lik, col = "steelblue", lwd = 2)

Now the distribution of mles (maximum likelihood estimates) seem to be normal.

Question 6

For this question you may again assume that the numerical values for \(\beta_0^C\), \(\beta^N_0\) and \(\beta_1^N\) that were given in question 2, are the true population parameter values. We now consider the log-likelihood function of question 2 as a function of \(\gamma\), say \(l(\gamma)\). In likelihood theory there is a result that says that in large samples, the following approximately holds:

\[ 2(l(\hat \gamma) − l(\gamma)) \stackrel{d}{=}\chi_1^2 \]

Upon using this result, construct a 95% confidence interval for the parameter \(\gamma\) and report this interval for the data from Table 2 (i.e. for the fitted model from question 3). How do you interpret this result and what is your final conclusion of your data analysis?

Solution

Note that \(2(l(\hat \gamma) − l(\gamma)) \stackrel{d}{=}\chi_1^2\) gives rise to the well known likelihood-ratio (LR) test.

In the LR test of the null hypothesis \[ H_0:\gamma = \gamma_0 \] versus the two-sided alternative \[ H_1:\gamma \neq \gamma_0 \] we would reject \(H_0\) at the \(\alpha\)-level if the LR statistic exceeds the \(100(1-\alpha/2)\)th or is below \(\alpha/2\) percentile of the \(\chi_1^2\) distribution. These values for \(\alpha=0.05\)-level are:

alpha = 0.05
lb = alpha/2
ub = 1-(alpha/2)
# quantiles
qchisq(c(lb,ub),1)

For our problem at with \(\hat \gamma = 1.28\) and let \(\gamma_0 = 0\)

# Note that the log_l function returns the negative log_l and the test statistics (LR)
# here is 
-2*(log_l(1.28)-log_l(0))

This value is is above the upper bound and hence \(\hat \gamma = 1.28\) is significantly non-zero.

We can construct the \(\alpha\) confidence interval as \[ \chi_{1,(\alpha/2)}^2\leq2(l(\hat \gamma) − l(\gamma)) \leq \chi_{1,(1-\alpha/2)}^2 \]

or \[ \frac{\chi_{1,(\alpha/2)}^2}{2}\leq (l(\hat \gamma) − l(\gamma)) \leq \frac{\chi_{1,(1-\alpha/2)}^2}{2} \]

which

qchisq(c(lb,ub),1)/2

Intepret this interval please!

LS0tCnRpdGxlOiAiUHJpbnN0YXRzIGhvbWV3b3JrIDEiCm91dHB1dDogaHRtbF9ub3RlYm9vawotLS0KSGVyZSBpcyB0aGUgbGluayB0byBnZXQgdGhlIGNvZGUgZnJvbSBnaXRodWIgCmh0dHBzOi8vZ2l0aHViLmNvbS9jaGFteWllbGRzL21hc3RhdHMvdHJlZS9tYWluL3BpbnN0YXRzXzIwMjAKCgojIyBRdWVzdGlvbnMKQ29uc2lkZXIgdGhlIHBhcGVyIG9mIERhdmlkIFNwaWVnZWxoYWx0ZXIgKGF2YWlsYWJsZSBvbiBVZm9yYSkuIEluIHRoaXMgcGFwZXIKdGhlIGF1dGhvciBleGFtaW5lcyB0aGUgYWdlLXNwZWNpZmljIHJpc2sgb2YgZHlpbmcgKHdpdGggb3IgZnJvbSkgQ292aWQtMTkgaW4KdGhlIFVLLiBUYWJsZSAyIGluIHRoZSBwYXBlciwgZ2l2ZXMgZm9yIHNldmVyYWwgYWdlIGNhdGVnb3JpZXMgdGhlIG51bWJlcnMKb2YgcmVwb3J0ZWQgQ292aWQtMTkgZGVhdGhzLCBudW1iZXJzIG9mIG5vbi1jb3ZpZCByZWxhdGVkIGRlYXRocywgcG9wdWxhdGlvbgpzaXplcywgYW1vbmcgb3RoZXIgbnVtYmVycy4KCgojIyAxLiBRdWVzdGlvbgoKQXNzaWduIHRvIGVhY2ggYWdlIGludGVydmFsIGEgbnVtZXJpY2FsIHZhbHVlIGdpdmVuIGJ5IHRoZSBtaWRwb2ludCAoZS5nLiBmb3IgMC00LCB0aGUgbWlkcG9pbnQgaXMgCigwKzQpLzIgPTIpIGFuZCBsZXQgJHBfaSQgZGVub3RlIHRoZSBjb3JyZXNwb25kaW5nCnBvcHVsYXRpb24gc2l6ZSBpbiB0aGUgVUsuCkFzc3VtZSB0aGF0IHRoZSBudW1iZXIgb2Ygbm9uLWNvdmlkIGRlYXRocyAkKFleTiApJCB3aXRoaW4gYW4gYWdlIGNsYXNzCndpdGggbWlkcG9pbnQgJGFfaSQgJChpID0gMSxcbGRvdHMgLCAxMSkkIGNhbiBiZSBkZXNjcmliZWQgYnkgYSBQb2lzc29uIGRpc3RyaWJ1dGlvbiB3aXRoIG1lYW4gZ2l2ZW4gYnkKCiQkClx0ZXh0e0V9KFleTnxcdGV4dHthZ2V9PWFfaSkgPSBwX2lcZXhwKFxiZXRhXzBeTitcYmV0YV8xXk5hX2kpCiQkCgoodGhlIHN1cGVyc2NyaXB0IHJlZmVycyB0byBub24tY292aWQpLgpBbHNvIGZvciB0aGUgY292aWQtcmVsYXRlZCBkZWF0aHMgJChZXkMpJCB3ZSBhc3N1bWUgYSBQb2lzc29uIGRpc3RyaWJ1dGlvbgp3aXRoCgokJApcdGV4dHtFfShZXkN8XHRleHR7YWdlfT1hX2kpID0gcF9pXGV4cChcYmV0YV8wXkMrXGdhbW1hXGJldGFfMV5OYV9pKQokJAoKKkhvdyBkbyB5b3UgaW50ZXJwcmV0IHRoZSAkXGdhbW1hJCBwYXJhbWV0ZXI/KgoKIyMjIFNvbHV0aW9uCgpCZXNpZGVzIHRoZSBmaXJzdCBhbmQgdGhlIGxhc3QgYWdlIGludGVydmFscywgdGhlIGRpZmZlcmVuY2UgYmV0d2VlbiB0aGUgbWlkIHBvaW50IGZyb20gb25lIGFnZSBncm91cCB0byB0aGUgb3RoZXIgaXMgMTAgeWVhcnMuIAoKKiBMZXRzIHRha2UgYSB1bml0IGluY3JlYXNlICBpbiBhZ2UgYXMgMTAgeWVhcnMuICBUaGVuIAokJApcYmV0YV8xXk4gPSBcbG9nIFx0ZXh0e0V9KFleTnxcdGV4dHthZ2V9ID0gYV9pKzEpIC0gXGxvZyBcdGV4dHtFfShZXk58XHRleHR7YWdlfSA9IGFfaSkKJCQKYW5kIAogJCQKXGdhbW1hXGJldGFfMV5OID0gXGxvZyBcdGV4dHtFfShZXkN8XHRleHR7YWdlfSA9IGFfaSsxKSAtIFxsb2cgXHRleHR7RX0oWV5DfFx0ZXh0e2FnZX0gPSBhX2kpCiQkCiogJFxiZXRhXzFeTiQgaXMgdGhlIGV4cGVjdGVkIGNoYW5nZSBpbiB0aGUgbnVtYmVyIG9mIG5vbi1jb3ZpZC0xOSBkZWF0aHMgcGVyIHVuaXQgaW5jcmVhc2UgaW4gYWdlIGluIHRoZSBsb2dhcml0aG1pYyBzY2FsZQoqICRcZ2FtbWEgXGJldGFfMV5OJCBpcyB0aGUgZXhwZWN0ZWQgY2hhbmdlIGluIHRoZSBudW1iZXIgb2YgY292aWQtMTkgZGVhdGhzIHBlciB1bml0IGluY3JlYXNlIGluIGFnZSBpbiB0aGUgbG9nYXJpdGhtaWMgc2NhbGUKICAgIAoqICRcZ2FtbWEgPSBcZ2FtbWFcYmV0YV8xXk4vXGJldGFfMV5OJCBpcyByYXRpbyBvZiB0aGUgZXhwZWN0ZWQgY2hhbmdlIGluIHRoZSBudW1iZXIgb2YgY292aWQtMTkgZGVhdGhzIG9uIHRoZSBleHBlY3RlZCBjaGFuZ2UgaW4gdGhlIG51bWJlciBvZiBub24tY292aWQgMTkgZGVhdGhzIGZvciBhIHVuaXQgaW5jcmVhc2UgaW4gYWdlIGluIHRoZSBsb2dhcml0aG1pYyBzY2FsZQoqICRcZ2FtbWE+MCQgaW1wbGllcyBmb3IgYSB1bml0IGluY3JlYXNlIGluIGFnZSB0aGUgZXhwZWN0ZWQgY2hhbmdlIGluIHRoZSBudW1iZXIgb2YgIGNvdmlkLTE5IGRlYXRocyBpcyBncmVhdGVyIHRoYW4gdGhhdCBvZiBub24tY292aWQgMTkgZGVhdGhzLiAgCiogJFxnYW1tYTwwJCBpbXBsaWVzIGZvciBhIHVuaXQgaW5jcmVhc2UgaW4gYWdlLCB0aGUgZXhwZWN0ZWQgY2hhbmdlIGluIHRoZSBudW1iZXIgb2YgIGNvdmlkLTE5IGRlYXRocyBpcyBsZXNzIHRoYW4gdGhhdCBvZiBub24tY292aWQgMTkgZGVhdGhzLiAKKiAkXGdhbW1hID0gMCQgaW1wbGllcyBmb3IgYSB1bml0IGluY3JlYXNlIGluIGFnZSwgdGhlIGV4cGVjdGVkIGNoYW5nZSBpbiB0aGUgbnVtYmVyIG9mICBjb3ZpZC0xOSBkZWF0aHMgaXMgZXF1YWwgdG8gIHRoYXQgb2Ygbm9uLWNvdmlkIDE5IGRlYXRocy4gIEhlbmNlIGR5aW5nIGZyb20gY292aWQgMTkgY29tcGFyZWQgdG8gZHlpbmcgZnJvbSBhIG5vbi1jb3ZpZCAxOSBjYXVzZSBpcyBpbmRlcGVuZGVudCBvZiBhZ2UuCgoKIyMgMi4gUXVlc3Rpb24KCkdpdmVuIHRoZSBtb2RlbHMgZnJvbSB0aGUgcHJldmlvdXMgcXVlc3Rpb24sIGFuZCBhc3N1bWluZyB0aGF0IGFsbCBudW0tCmJlcnMgb2YgZGVhdGhzIChib3RoIGNvdmlkIGFuZCBub24tY292aWQpIGFyZSBpbmRlcGVuZGVudGx5IGRpc3RyaWJ1dGVkLiAgQ29uc3RydWN0IHRoZSBsb2ctbGlrZWxpaG9vZCBmdW5jdGlvbiBhbmQgbWFrZSBhIGdyYXBoIG9mIHRoZSBsb2ctbGlrZWxpaG9vZCBhcyBhIGZ1bmN0aW9uIG9mIHRoZSAkXGdhbW1hJCBwYXJhbWV0ZXIuIFlvdSBtYXkgdXNlIHRoZSBmb2xsb3dpbmcgZXN0aW1hdGVzIGZvciB0aGUgb3RoZXIgcGFyYW1ldGVyczoKCiQkClxiZXRhXzBeTiA9IOKIkjEyLjA2IH5+IFxiZXRhXzBeQyA9IOKIkjE0LjE1IH5+IFxiZXRhXzFeTiA9IDAuMDk4MzYKJCQKCiMjIyBTb2x1dGlvbiAKV2UgaGF2ZSBiZWVuIHRvbGQgdGhhdCAgJFleTiQgYW5kICRZXkMkIGZvbGxvdyBhIFBvaXNzb24gZGlzdHJpYnV0aW9uLCByZWNhbGwgdGhhdCBpZiBhIHJhbmRvbSB2YXJpYWJsZSAkSyQgZm9sbG93cyBhIFBvaXNzaW9uIGRpc3RyaWJ1dGlvbiwgaXQgaGFzIHRoZSBmb2xsb3cgbWFzcyBmdW5jdGlvbiAgCgokJApQX1xsYW1iZGEoSz1rKSA9IGYoa3xcbGFtYmRhKSA9IFxmcmFje1xsYW1iZGFee2t9ZV57LVxsYW1iZGF9fXtrIX0KJCQKTGV0J3Mgc2F5IHdlIGhhdmUgb2JzZXJ2ZWQgJG4kIHZhbHVlcyBmcm9tICRuJCByYW5kb20gdmFyaWFibGVzICRLXzEsXGxkb3RzLEtfbiQgdGhhdCBhcmU6CgoqIGFsbCBpZGVudGljYWxseSBkaXN0cmlidXRlZCAoaS5lLiB0aGV5IGFsbCBmb2xsb3cgYSBQb2lzc29uIGRpc3RyaWJ1dGlvbiB3aXRoIHBhcmFtZXRlciAkXGxhbWJkYV9pJCwgd2VsbCBub3QgZXhhY3RseSBpZGVudGljYWwgc2luY2UgdGhlaXIgbWVhbiAkXGxhbWJkYSRzIGFyZSBkaWZmZXJlbnQpIAoqIGluZGVwZW5kZW50LCBpLmUsICRQKEtfMSxcbGRvdHMsS19uKSA9IFxwcm9kX2kgUChLX2kpJAoKVGhlIGxpa2VsaWhvb2QgaXMgdGhlbiBkZWZpbmVkIGFzIGEgZnVuY3Rpb24gb2YgJFxtYXRoYmYgXExhbWJkYSA9IChcbGFtYmRhXzEsXGxkb3RzLFxsYW1iZGFfbikkIGdpdmVuIHRoZSBkYXRhLCBpLmUuLAokJApMKFxtYXRoYmYgXExhbWJkYXxLXzE9a18xLFxsZG90cyxLX249a19uKSA9IFxwcm9kX2kgUF97XGxhbWJkYV9pfShLX2k9a19pKSA9IFxwcm9kX2lmKGtfaXxcbGFtYmRhX2kpID0gXHByb2RfaVxmcmFje1xsYW1iZGFfaV57a19pfWVeey1cbGFtYmRhX2l9fXtrX2khfQokJApUaGUgbG9nIGxpa2VsaWhvb2Qgd2hpY2ggaXMgc2ltcGxlciB0cmFuc2Zvcm1hdGlvbiB0byB3b3JrIHdpdGggaXMgZ2l2ZW4gYnkgCiQkCmwoXExhbWJkYXxLXzE9a18xLFxsZG90cyxLX249a19uKSA9IFxsb2cgXHByb2RfaSBQX3tcbGFtYmRhX2l9KEtfaT1rX2kpICA9IFxzdW1faVxsb2dcZnJhY3tcbGFtYmRhX2lee2tfaX1lXnstXGxhbWJkYV9pfX17a19pIX0KJCQKCgokJApsKFxMYW1iZGF8S18xPWtfMSxcbGRvdHMsS19uPWtfbikgID0gXHN1bV9pICBcYmlnKGtfaSBcbG9nXGxhbWJkYV9pIC0gXGxhbWJkYV9pICAtIFxsb2cga19pIVxiaWcpCiQkCgoKJCQKbChcTGFtYmRhfEtfMT1rXzEsXGxkb3RzLEtfbj1rX24pICA9IFxzdW1faSBrX2kgXGxvZ1xsYW1iZGFfaSAtIFxzdW1faSBcbGFtYmRhX2kgIC0gXHN1bV9pIFxsb2cga19pIQokJAoKRm9yIHRoZSBwcm9ibGVtIGF0IGhhbmQgd2UgaGF2ZToKCiogJFxsYW1iZGFfaV5OID0gXHRleHR7RX0oWV5OfFx0ZXh0e2FnZX09YV9pKSQgZm9yIG5vbi1jb3ZpZCAxOSBkZWFkcwoqICRcbGFtYmRhX2leQyAoXGdhbW1hKSA9IFx0ZXh0e0V9KFleQ3xcdGV4dHthZ2V9PWFfaSkkIGZvciBjb3ZpZCAxOSBkZWFkcwoqIG91ciBwYXJhbWV0ZXIgb2YgaW50ZXJlc3QgaXMgICRcZ2FtbWEkLiAKKiBzYW1wbGVzIHNpemUgICRuX049MTEkIGZvciB0aGUgY292aWQtMTkgZGVhZHMgCiogc2FtcGxlcyBzaXplICAkbl9DPTExJCBmb3IgdGhlIG5vbi1jb3ZpZC0xOSBkZWFkcwoKJCQKbChcZ2FtbWF8WV5DXzE9WV5DXzEsXGxkb3RzLFleQ19uPVleQ19uLFleTl8xPXleTl8xLFxsZG90cyxZXk5fbj15Xk5fbikgID0gXGxvZyBcUGlfaWYoWV5DfFxsYW1iZGFfaV5DKFxnYW1tYSkpZih5X2leTnxcbGFtYmRhX2leTikKJCQKJCQKbChcZ2FtbWF8WV5DXzE9WV5DXzEsXGxkb3RzLFleQ19uPVleQ19uLFleTl8xPXleTl8xLFxsZG90cyxZXk5fbj15Xk5fbikgID0gXHN1bV97aT0xfV57MTF9XGxvZyBmKFleQ3xcbGFtYmRhX2leQyhcZ2FtbWEpKStcc3VtX3tpPTF9XnsxMX1cbG9nIGYoeV9pXk58XGxhbWJkYV9pXk4pCiQkCiQkCmwoXGdhbW1hfFleQ18xPVleQ18xLFxsZG90cyxZXkNfbj1ZXkNfbixZXk5fMT15Xk5fMSxcbGRvdHMsWV5OX249eV5OX24pICA9IFxzdW1fe2k9MX1eezExfVxiaWcoWV5DXGxvZyBcbGFtYmRhX2leQyhcZ2FtbWEpICsgeV9pXk5cbG9nIFxsYW1iZGFfaV5OXGJpZykgLVxzdW1fe2k9MX1eezExfVxiaWcoXGxhbWJkYV9pXkMoXGdhbW1hKStcbGFtYmRhX2leTlxiaWcpLVxzdW1fe2k9MX1eezExfVxsb2cgKFleQ3lfaV5OKQokJApXZSBjYW4gc2ltcGx5IHdyaXRlIG91ciBsb2cgbGlrZWxpaG9vZCBmdW5jdGlvbiBhcyAKCiQkCmwoXGdhbW1hKSAgPSBcc3VtX3tpPTF9XnsxMX1cYmlnKFleQ1xsb2cgXGxhbWJkYV9pXkMoXGdhbW1hKSArIHlfaV5OXGxvZyBcbGFtYmRhX2leTlxiaWcpIC1cc3VtX3tpPTF9XnsxMX1cYmlnKFxsYW1iZGFfaV5DKFxnYW1tYSkrXGxhbWJkYV9pXk5cYmlnKS1cc3VtX3tpPTF9XnsxMX1cbG9nIChZXkNZXk4pCiQkClNvIHdlIGhhdmUgb3VyIGxvZyBsaWtlbGlob29kIGZ1bmN0aW9uIGxldHMgY29kZSBpdCBpbiBgUmAuIAoKCmBgYHtyfQpsaWJyYXJ5KGRwbHlyLAogICAgICAgIHZlcmJvc2U9RkFMU0UpCmxpYnJhcnkodGlkeXZlcnNlLAogICAgICAgIHZlcmJvc2UgPSBGQUxTRSkKCmFnZV9taWRfcG9pbnQgPSBmdW5jdGlvbih4LHkpewogICh4K3kpLzIKfQoKYmV0YV8wX0NfaGF0ID0gLTE0LjE1CmJldGFfMV9OX2hhdCA9IDAuMDk4MzYKYmV0YV8wX05faGF0ID0gLTEyLjA2CgpsYiA9IGMoMCw1LDE1LDI1LDM1LDQ1LDU1LDY1LDc1LDg1KQp1YiA9IGMoNCwxNCwyNCwzNCw0NCw1NCw2NCw3NCw4NCw4OSkKCmFnZSA9IGMoYWdlX21pZF9wb2ludChsYix1YiksOTApCgpZX0MgPSBjKDMsMywzMywxMjgsMzY5LDEyODMsMzQ3Niw3MzE5LDE2MDQzLDEwMTYwLDEwNzkwKQoKcG9wdWxhdGlvbiA9IGMoMzUzNTQzMCw3MTU5MTAyLDY5ODg3NTUsCiAgICAgICAgICAgICAgIDc5OTgzMDIsNzQ2MDg1Niw4MTQyNTI4LAogICAgICAgICAgICAgICA3MDE5NTkwLDU5MDY5MjgsMzQ3NjkyMiwKICAgICAgICAgICAgICAgOTE4NDM3LDUyODk1OSkKCllfTiA9IGMoODQ4LDEzOCw0NzAsMTE0MSwyNTc3LDY1NzcsMTM1NjUsMjcxODQsNDc3MjksMzA3MDMsMzA3MDMpCgpkYXRhID0gZGF0YS5mcmFtZShhZ2UsWV9OLFlfQyxwb3B1bGF0aW9uKQoKCmJldGFfMF9DID0gYmV0YV8wX0NfaGF0CmJldGFfMF9OID0gYmV0YV8wX05faGF0CmJldGFfMV9OID0gYmV0YV8xX05faGF0CgoKbG9nX2wgPSBmdW5jdGlvbihnYW1tYV9oYXQpewoKICBsYW1iZGFfTiA9IGRhdGEkcG9wdWxhdGlvbipleHAoYmV0YV8wX04rYmV0YV8xX04qZGF0YSRhZ2UpCiAgbGFtYmRhX0MgPSBkYXRhJHBvcHVsYXRpb24qZXhwKGJldGFfMF9DK2dhbW1hX2hhdCpiZXRhXzFfTipkYXRhJGFnZSkKICAKICBzdW1fMSA9IGRhdGEkWV9DKmxvZyhsYW1iZGFfQykrZGF0YSRZX04qbG9nKGxhbWJkYV9OKQogIHN1bV8yID0gbGFtYmRhX0MrbGFtYmRhX04KICBzdW1fMyA9IGxvZyhkYXRhJFlfQypkYXRhJFlfTikKICAKICBzdW1fYWxsPSBzdW0oc3VtXzEtc3VtXzItc3VtXzMpCiAgcmV0dXJuKC1zdW1fYWxsKQp9CmRhdGEKYGBgCgpgYGB7cn0KbG9nX2xzID0gYygpCmdhbW1hcyA9IHNlcSgxLDEuMixsZW5ndGgub3V0ID0gMTAwMCkKZm9yKGdhbW1hIGluIGdhbW1hcyl7CiAgbG9nX2xzICA9IGMobG9nX2woZ2FtbWEpLGxvZ19scykKfQoKbG9nX2xzID0gbG9nX2xzL21heChhYnMobG9nX2xzKSkKCnBsb3QoeD1nYW1tYXMseT0tbG9nX2xzLHR5cGU9ImwiKQphYmxpbmUodj1nYW1tYXNbLWxvZ19scz09bWF4KC1sb2dfbHMpXSkKYGBgCgoKYGBge3J9CnBhc3RlMCgnb3B0aW1hbCBnYW1tYTogJyxyb3VuZChnYW1tYXNbLWxvZ19scyA9PSBtYXgoLWxvZ19scyldLDMpKQpgYGAKIyMgMy5RdWVzdGlvbiAKVXNlIHRoZSBvcHRpbSAob3Igc2ltaWxhcikgZnVuY3Rpb24gaW4gUiBmb3IgZmluZGluZyB0aGUgbWF4aS0KbXVtIGxpa2VsaWhvb2QgZXN0aW1hdGUgb2YgJFxnYW1tYSQuCgojIyMgU29sdXRpb24gCkxldHMgbG9vayBmb3IgJFxnYW1tYSQgd2l0aCBgb3B0aW1gCgpgYGB7cn0KIyBpbml0aWFsIHZhbHVlIG9mIGdhbW1hCnBhciA9IDAKCm9wdCA9IG9wdGltKHBhcj1wYXIsCiAgICAgIGZuPSBsb2dfbCkKCm9wdApgYGAKCkNhbiB5b3UgY29tcGFyZSB0aGUgcmVzdWx0cyBvZiB0aGUgbWFudWFsIGNvbXB1dGF0aW9uIHdpdGggdGhhdCBvZiBgb3B0aW1gLiBUaGV5IHNlZW0gdG8gYmUgZGlmZmVyZW50PyBQbGVhc2UgY2hlY2sgaWYgdGhlcmUgYXJlIGFueSBtaXN0YWtlcyBpbiBteSBjb21wdXRhdGlvbnMgCmBgYHtyfQpwbG90KHg9Z2FtbWFzLHk9LWxvZ19scyx0eXBlPSJsIikKYWJsaW5lKHY9Z2FtbWFzWy1sb2dfbHM9PW1heCgtbG9nX2xzKV0sY29sPSdibHVlJykKYWJsaW5lKHY9b3B0JHBhcixjb2w9J3JlZCcpCmBgYAoKCiogTm90ZSB0aGF0IG91ciBgbG9nX2xgIGZ1bmN0aW9uIHJldHVybnMgdGhlIGBuZWdhdGl2ZWAgbG9nIGxpa2VsaWhvb2QgaW5zdGVhZCBiZWNhdXNlIGBvcHRpbWAgaXMgYSBtaW5pbWlzYXRpb24gZnVuY3Rpb24uIAoKKiBJIGNoZWNrZWQgdGhlIHBhcGVyIGZvciB0aGUgbWF4aW11bSBsaWtlbGlob29kIGVzdGltYXRvciBvZiAkXGdhbW1hJCwgdGhleSBhbHNvIGhhdmUgJCgxLjEyOCkkLCB0aGUgdmFsdWUgZnJvbSB0aGUgYG9wdGltYCBmdW5jdGlvbi4gU28gZW1tLi4uLiBsZXQncyBjaGVjayB0aGUgYG9wdGltaXNlYCBmdW5jdGlvbiA7KQpgYGB7cn0Kb3B0aW1pemUobG9nX2wsYygtMiwyKSkKYGBgCgojIyA0LiBRdWVzdGlvbgoKd2l0aCB0aGUgaW5mb3JtYXRpb24gaW4gVGFibGUgMiBhbmQgd2l0aCB0aGUgbW9kZWxzIGFuZCB0aGVpcgpwYXJhbWV0ZXIgZXN0aW1hdGVzIG9mIHRoZSBwcmV2aW91cyBxdWVzdGlvbnMsIGVzdGltYXRlIHRoZSBmb2xsb3dpbmcgdHdvCnByb2JhYmlsaXRpZXM6CiQkClx0ZXh0e1B9IChcdGV4dHtBZ2V9ID0gMTkuNSB8IFx0ZXh0eyBkaWVkIG9mIGNvdmlkfSkgXHRleHR7IGFuZCB9IFx0ZXh0e1B9IChcdGV4dHtBZ2V9ID0gNzkuNSB8IFx0ZXh0eyBkaWVkIG9mIGNvdmlkfSkKJCQKCiMjIyBTb2x1dGlvbiAKQmF5ZXMgdGhlb3JlbTogZm9yIHR3byByYW5kb20gdmFyaWFibGVzICRYJCBhbmQgJFkkLAokJApQKFh8WSkgPSBcZnJhY3tQKFl8WClQKFgpfXtQKFkpfQokJAoKSGVuY2UgCgokJApcdGV4dHtQfSAoXHRleHR7QWdlfSA9IDE5LjUgfCBcdGV4dHsgZGllZCBvZiBjb3ZpZH0pICA9IFxmcmFje1x0ZXh0e1B9ICggXHRleHR7IGRpZWQgb2YgY292aWR9IHwgXHRleHR7QWdlfSA9IDE5LjUgKSBcdGV4dHtQfSAoXHRleHR7QWdlfSA9IDE5LjUpfXtcdGV4dHtQfSggXHRleHR7IGRpZWQgb2YgY292aWR9KX0KJCQKCiogZnVydGhlciwgdXNlIHRoZXNlIHZhbHVlcyBhbmQgcGx1ZyBpbnRvIHRoZSBmb3JtdWxhIGFib3ZlLgoKJCQKXHRleHR7UH0gKCBcdGV4dHsgZGllZCBvZiBjb3ZpZH0gfCBcdGV4dHtBZ2V9ID0gMTkuNSApID0gXGZyYWN7XHRleHR7RX0oWV5DfFx0ZXh0e0FnZX0gPSAxOS41KX17XHRleHR7cG9wdWxhdGlvbiBzaXplIHdpdGggbWlkIHBvaW50IGF0IGFnZSAxOS41fX0KJCQKJCQKIFx0ZXh0e1B9IChcdGV4dHtBZ2V9ID0gMTkuNSkgPSBcZnJhY3tcdGV4dHtQb3B1bGF0aW9uIHNpemUgd2l0aCBtaWQgcG9pbnQgYXQgMTkuNX19e1x0ZXh0e1RvdGFsIHBvcHVsYXRpb24gc2l6ZSBhY3Jvc3MgYWxsIGFnZSBncm91cHN9fQokJAoKJCQKIFx0ZXh0e1B9IChcdGV4dHsgZGllZCBvZiBjb3ZpZH0pID0gXGZyYWN7XHRleHR7VG90YWwgbnVtYmVyIG9mIHBlb3BsZSB3aG8gZGllZCBmcm9tIGNvdmlkIGFjcm9zcyBhbGwgYWdlIGdyb3Vwc319e1x0ZXh0e1RvdGFsIHBvcHVsYXRpb24gc2l6ZSBhY3Jvc3MgYWxsIGFnZSBncm91cHN9fQokJAoKCkZpbGwgdGhlIHZhbHVlcyBhbmQgY29tcHV0ZSB0aGlzIHByb2JhYmlsaXR5LCBkbyB0aGUgc2FtZSBmb3IgJFx0ZXh0e1B9IChcdGV4dHtBZ2V9ID0gNzkuNSB8IFx0ZXh0eyBkaWVkIG9mIGNvdmlkfSkkCgojIyBRdWVzdGlvbiA1LiAKRm9yIHRoaXMgcXVlc3Rpb24geW91IG1heSBhc3N1bWUgdGhhdCB0aGUgbnVtZXJpY2FsIHZhbHVlcyBmb3IgKHRoZSBlc3RpbWF0ZXMpICRcYmV0YV8wXkMkLCAkXGJldGFeTl8wJCBhbmQgJFxiZXRhXzFeTiQgdGhhdCB3ZXJlIGdpdmVuIGluIHF1ZXN0aW9uIDIsIGFyZSB0aGUgdHJ1ZSBwb3B1bGF0aW9uIHBhcmFtZXRlciB2YWx1ZXMuU2V0IHVwIGEgc2ltdWxhdGlvbiBzdHVkeSBpbiB3aGljaCB5b3Ugc2ltdWxhdGUgZGF0YSAoYXMgaW4gVGFibGUgMikgZm9yIHRoZSBjb3ZpZCBhbmQgbm9uLWNvdmlkIHJlbGF0ZWQgZGVhdGhzLCBhbmQgY2FsY3VsYXRlIHRoZQptYXhpbXVtIGxpa2VsaWhvb2QgZXN0aW1hdGUgb2YgJFxnYW1tYSQgZm9yIGVhY2ggc2ltdWxhdGVkIGRhdGFzZXQuCgoKKiBQbG90IGEgaGlzdG9ncmFtIG9mIHRoZSBlc3RpbWF0ZXMgb2YgJFxnYW1tYSQuIEhvdyBkbyB5b3UgaW50ZXJwcmV0IHRoaXMKaGlzdG9ncmFtPwoqIEFzc2VzcyB3aGV0aGVyIHRoZSBlc3RpbWF0ZXMgYWdyZWUgd2l0aCBhIG5vcm1hbCBkaXN0cmlidXRpb24uCgojIyMgU29sdXRpb24gCgpgYGB7ciB3YXJuaW5nPUZBTFNFfQpuPTExCmdhbW1hX2hhdCA9IDEuMgoKbGFtYmRhX04gPSBkYXRhJHBvcHVsYXRpb24qZXhwKGJldGFfMF9OK2JldGFfMV9OKmRhdGEkYWdlKQpsYW1iZGFfQyA9IGRhdGEkcG9wdWxhdGlvbipleHAoYmV0YV8wX0MrZ2FtbWFfaGF0KmJldGFfMV9OKmRhdGEkYWdlKQoKbWF4X2xpa19lc3QgPSBjKCkKbnVtX3NpbXVsYXRpb25zID0gMTAwMAoKZm9yKGsgaW4gMTpudW1fc2ltdWxhdGlvbnMpewogIAogICMgc2V0LnNlZWQoaykKICAKICBZX0MgPSBycG9pcyhuLGxhbWJkYV9DKQogIFlfTiA9IHJwb2lzKG4sbGFtYmRhX04pCiAgZGF0YSA9IGRhdGEuZnJhbWUoYWdlLFlfTixZX0MscG9wdWxhdGlvbikKICAKICBwYXIgPSAxCiAgCiAgb3B0ID0gb3B0aW1pemUobG9nX2wsYygtMS41LDEuNSkpOwogIAogIG1heF9saWtfZXN0W2tdID0gb3B0JG1pbmltdW0KfQptPC1tZWFuKG1heF9saWtfZXN0KQpzdGQ8LXNxcnQodmFyKG1heF9saWtfZXN0KSkKCmhpc3QobWF4X2xpa19lc3QsIAogICAgIGNvbCA9ICdza3libHVlMycsCiAgICAgYnJlYWtzID0gMjAsCiAgICAgcHJvYj1UUlVFKQpjdXJ2ZShkbm9ybSh4LCBtZWFuPW0sIHNkPXN0ZCksIAogICAgICBjb2w9InJlZCIsIAogICAgICBsd2Q9MywgCiAgICAgIGFkZD1UUlVFLCAKICAgICAgeWF4dD0ibiIpCgpwcmludCgnJykKYGBgCkFzc2Vzc2luZyBub3JtYWxpdHkKYGBge3J9CnFxbm9ybShtYXhfbGlrX2VzdCwgcGNoID0gMSwgZnJhbWUgPSBGQUxTRSkKcXFsaW5lKG1heF9saWtfZXN0LCBjb2wgPSAic3RlZWxibHVlIiwgbHdkID0gMikKYGBgCk9rIGl0IHNlZW1zIGxpa2UgdGhlcmUgYXJlIHR3byBzZXRzIG9mIG1heGltdW0gbGlrZWxpaG9vZCBlc3RpbWF0ZXM6T25lIHRoYXQgb2NjdXJzIGF0IHRoZSB1cHBlciBib3VuZCBvZiB0aGUgaW50ZXJ2YWwgb2YgcG9zc2libGUgJFxnYW1tYSQgdmFsdWVzIGFuZCB0aGUgb3RoZXJzLiBUaGUgdXBwZXIgYm91bmQgb2YgdGhlIGludGVydmFsIGlzIHRha2VuIGFzIHRoZSBtYXhpbXVtIGxpa2VsaWhvb2QgZXN0aW1hdGUgb25seSB3aGVuIGl0IGNhbiBub3QgYmUgY29tcHV0ZWQuIApXZSB3aWxsIHRha2UgdGhlIHVwcGVyIGJvdW5kIHZhbHVlcyBvdXQgb2YgdGhlIGRpc3RyaWJ1dGlvbiBhbmQgcmVhc3Nlc3MuIApgYGB7cn0KIyMjIHRha2luZyBvdXQgbWF4X2xpayB0aGF0IGFyZSBncmVhdGVyIDEuNApzdWJfbWF4X2xpayA9IG1heF9saWtfZXN0W21heF9saWtfZXN0PDEuNF0KCm08LW1lYW4oc3ViX21heF9saWspCnN0ZDwtc3FydCh2YXIoc3ViX21heF9saWspKQoKaGlzdChzdWJfbWF4X2xpaywgCiAgICAgY29sPSAnc2t5Ymx1ZTMnLAogICAgIGJyZWFrcyA9IDIwLAogICAgIHByb2I9VFJVRSkKCmN1cnZlKGRub3JtKHgsIG1lYW49bSwgc2Q9c3RkKSwgCiAgICAgIGNvbD0icmVkIiwgbHdkPTMsIGFkZD1UUlVFLCB5YXh0PSJuIikKYWJsaW5lKHY9bWVhbihzdWJfbWF4X2xpayksY29sPSJwaW5rIixsd2Q9MykKYWJsaW5lKHY9Z2FtbWFfaGF0LGNvbD0iZ3JlZW4iLGx3ZD0zKQpgYGAKCmBgYHtyfQpxcW5vcm0oc3ViX21heF9saWssIHBjaCA9IDEsIGZyYW1lID0gRkFMU0UpCnFxbGluZShzdWJfbWF4X2xpaywgY29sID0gInN0ZWVsYmx1ZSIsIGx3ZCA9IDIpCmBgYAoKTm93IHRoZSBkaXN0cmlidXRpb24gb2YgbWxlcyAobWF4aW11bSBsaWtlbGlob29kIGVzdGltYXRlcykgc2VlbSB0byBiZSBub3JtYWwuCgojIyBRdWVzdGlvbiA2CkZvciB0aGlzIHF1ZXN0aW9uIHlvdSBtYXkgYWdhaW4gYXNzdW1lIHRoYXQgdGhlIG51bWVyaWNhbCB2YWx1ZXMgZm9yICRcYmV0YV8wXkMkLCAkXGJldGFeTl8wJCBhbmQgJFxiZXRhXzFeTiQgCnRoYXQgd2VyZSBnaXZlbiBpbiBxdWVzdGlvbiAyLCBhcmUgdGhlIHRydWUgcG9wdWxhdGlvbgpwYXJhbWV0ZXIgdmFsdWVzLiBXZSBub3cgY29uc2lkZXIgdGhlIGxvZy1saWtlbGlob29kIGZ1bmN0aW9uIG9mIHF1ZXN0aW9uIDIgYXMgYSBmdW5jdGlvbiBvZiAkXGdhbW1hJCwgc2F5ICRsKFxnYW1tYSkkLgpJbiBsaWtlbGlob29kIHRoZW9yeSB0aGVyZSBpcyBhIHJlc3VsdCB0aGF0IHNheXMgdGhhdCBpbiBsYXJnZSBzYW1wbGVzLCB0aGUKZm9sbG93aW5nIGFwcHJveGltYXRlbHkgaG9sZHM6CgokJAoyKGwoXGhhdCBcZ2FtbWEpIOKIkiBsKFxnYW1tYSkpIFxzdGFja3JlbHtkfXs9fVxjaGlfMV4yCiQkCgpVcG9uIHVzaW5nIHRoaXMgcmVzdWx0LCBjb25zdHJ1Y3QgYSA5NVwlIGNvbmZpZGVuY2UgaW50ZXJ2YWwgZm9yCnRoZSBwYXJhbWV0ZXIgJFxnYW1tYSQgYW5kIHJlcG9ydCB0aGlzIGludGVydmFsIGZvciB0aGUgZGF0YSBmcm9tIFRhYmxlIDIgKGkuZS4gZm9yCnRoZSBmaXR0ZWQgbW9kZWwgZnJvbSBxdWVzdGlvbiAzKS4gSG93IGRvIHlvdSBpbnRlcnByZXQgdGhpcyByZXN1bHQgYW5kCndoYXQgaXMgeW91ciBmaW5hbCBjb25jbHVzaW9uIG9mIHlvdXIgZGF0YSBhbmFseXNpcz8KCiMjIyBTb2x1dGlvbgpOb3RlIHRoYXQgJDIobChcaGF0IFxnYW1tYSkg4oiSIGwoXGdhbW1hKSkgXHN0YWNrcmVse2R9ez19XGNoaV8xXjIkIGdpdmVzIHJpc2UgdG8gdGhlIHdlbGwga25vd24gbGlrZWxpaG9vZC1yYXRpbyAoTFIpIHRlc3QuCgpJbiB0aGUgTFIgdGVzdCBvZiB0aGUgbnVsbCBoeXBvdGhlc2lzCiQkCkhfMDpcZ2FtbWEgPSBcZ2FtbWFfMAokJAp2ZXJzdXMgdGhlIHR3by1zaWRlZCBhbHRlcm5hdGl2ZQokJApIXzE6XGdhbW1hIFxuZXEgXGdhbW1hXzAKJCQKd2Ugd291bGQgcmVqZWN0ICRIXzAkIGF0IHRoZSAkXGFscGhhJC1sZXZlbCBpZiB0aGUgTFIgc3RhdGlzdGljIGV4Y2VlZHMgdGhlICQxMDAoMS1cYWxwaGEvMikkdGggb3IgaXMgYmVsb3cgJFxhbHBoYS8yJCAgcGVyY2VudGlsZSAgb2YgdGhlICRcY2hpXzFeMiQgIGRpc3RyaWJ1dGlvbi4gVGhlc2UgdmFsdWVzIGZvciAkXGFscGhhPTAuMDUkLWxldmVsIGFyZToKYGBge3J9CmFscGhhID0gMC4wNQpsYiA9IGFscGhhLzIKdWIgPSAxLShhbHBoYS8yKQojIHF1YW50aWxlcwpxY2hpc3EoYyhsYix1YiksMSkKYGBgCkZvciBvdXIgcHJvYmxlbSBhdCAgd2l0aCAkXGhhdCBcZ2FtbWEgPSAxLjI4JCBhbmQgbGV0ICRcZ2FtbWFfMCA9IDAkCmBgYHtyfQojIE5vdGUgdGhhdCB0aGUgbG9nX2wgZnVuY3Rpb24gcmV0dXJucyB0aGUgbmVnYXRpdmUgbG9nX2wgYW5kIHRoZSB0ZXN0IHN0YXRpc3RpY3MgKExSKQojIGhlcmUgaXMgCi0yKihsb2dfbCgxLjI4KS1sb2dfbCgwKSkKYGBgCgpUaGlzIHZhbHVlIGlzIGlzIGFib3ZlIHRoZSB1cHBlciBib3VuZCBhbmQgaGVuY2UgJFxoYXQgXGdhbW1hICA9IDEuMjgkIGlzIHNpZ25pZmljYW50bHkgbm9uLXplcm8uCgpXZSBjYW4gY29uc3RydWN0IHRoZSAkXGFscGhhJCBjb25maWRlbmNlIGludGVydmFsIGFzIAokJApcY2hpX3sxLChcYWxwaGEvMil9XjJcbGVxMihsKFxoYXQgXGdhbW1hKSDiiJIgbChcZ2FtbWEpKSBcbGVxIFxjaGlfezEsKDEtXGFscGhhLzIpfV4yCiQkCgoKb3IgCiQkClxmcmFje1xjaGlfezEsKFxhbHBoYS8yKX1eMn17Mn1cbGVxIChsKFxoYXQgXGdhbW1hKSDiiJIgbChcZ2FtbWEpKSBcbGVxIFxmcmFje1xjaGlfezEsKDEtXGFscGhhLzIpfV4yfXsyfQokJAoKd2hpY2ggCmBgYHtyfQpxY2hpc3EoYyhsYix1YiksMSkvMgpgYGAKCkludGVwcmV0IHRoaXMgaW50ZXJ2YWwgcGxlYXNlISAKCg==