This post provides code to model variance components in a multi-level setting. This is useful when variances are heteroskedastic and, we suspect, heteroskedasticity is driven by some observed variable(s).

Steps:

  1. Load the data. For this demonstration I will simulate data in which 600 individual (\(i\)) are nested into 200 groups (\(j\)). \(Y\) is the outcome variable and I allow for random intercepts at the group level plus structured dispersion at both group-level and individual-level. In this particular case the variance components are functions of the same variable \(z\), but this predictor has opposite effects on each of them.

More formally, the DGP can be described as: \(Y = \mathbf{1}_{k} \otimes \mathbf{1}_{n}\mu + \mathbf{I}_{k} \otimes \mathbf{1}_{n} \mathbf{a} + \mathbf{I}_{k} \otimes \mathbf{I}_{n} \mathbf{e}\), where \(\mathbf{a}\) and \(\mathbf{e}\) are random effects, such that \(\mathbf{a} \sim N(\mathbf{0}, \mathbf{\sigma_{a}})\) and \(\mathbf{e} \sim N(\mathbf{0}, \mathbf{\sigma_{e}})\). Moreover, both variances are functions of \(z\): \(\mathbf{\sigma_{a}}=f(\mathbf{z})\) and \(\mathbf{\sigma_{e}}=g(\mathbf{z})\).

n=3   # Number of individuals by group
k=200 # Number of  groups
# Design matrices for multi-levels
One.k=matrix(rep(1,k))
One.n=matrix(rep(1,n))
I.k=diag(k)
I.n=diag(n)
mu=0
# variance predictor 
z = runif(k,1,5)               # indep variable in variance function at group level
Z = (I.k%x%One.n)%*%z          # indep variable in variance function at individual level 
# An arbitrary function for variance at the group level
THETA_a0 =  -5
THETA_a1  = 1.9
var.a = exp(THETA_a0 + THETA_a1*z + rnorm(k))
sd.a  = sqrt(var.a)
# An arbitrary function for variance at individual level (residual variance)
THETA_e0 =  5
THETA_e1  = -2.7
var.e = exp(THETA_e0 + THETA_e1*Z + rnorm(k*n))
sd.e  = sqrt(var.e)
# Random intercepts
a=rnorm(k,0,sd.a)
# Random error 
e=rnorm(k*n,0,sd.e) 
# Creates Y = mu + random intercept + random error
Y = (One.k%x%One.n)*mu + (I.k%x%One.n)%*%a + (I.k%x%I.n)%*%e   
# Generate indicators. To be used later. 
group=matrix(rep(1:k))  # vector with group indicator for each group
Group=(I.k%x%One.n)%*%group # vector with Group indicator for each individual

A simple scatterplot of \(Y\) and \(z\) should reveal variance structure.

  1. Load rstan to perform Bayesian estimation via Halmiltonian Monte Carlo. Put the data into a list.
library("rstan")
Data_l1 <- data.frame(Y,Group,Z) # data individual-level
Data_l2 <- data.frame(group,z)   # data group-level 
data    <- list(N=n*k,J=k,y=Data_l1$Y,G=Data_l1$Group,Z=Data_l1$Z, g=Data_l2$group,z=Data_l2$z)
  1. Write the model in stan.
structured_dispersion <- '
data {
  int<lower=0> J;                   // number of groups 
  int<lower=0> N;                   // number of individuals
  int<lower=1, upper=J>  G[N];      // map individuals to groups
  vector[N] y;                      // outcome variable
  vector[J] z;                      // group level dispersion predictor
  vector[N] Z;                      // individual level dispersion predictor
}
parameters {
  vector[J] a;                           // Random Intercept           
  
  real theta_a0;                         // Intercept model for variance of random intercept
  real theta_e0;                         // Intercept model for variance of residual error
  
  real theta_a1;                         // Slope model for variance of random intercept
  real theta_e1;                         // Slope model for variance of residual error
  vector<lower=0>[J] sigma2_a;           // heterogeneous var for random intercept 
  vector<lower=0>[N] sigma2_e;           // heterogeneous var residual error
}
transformed parameters {
  
  vector[N] y_mu;                        // Random effects
  vector[J] logsigma2_a_mu;              // mean of log-variance for random intercept 
  vector[J] sigma2_a_mu;                 // mean of variance for random intercept 
  vector[J] sigma_a;                     // var for random intercept 
  vector[N] logsigma2_e_mu;              // mean of log-variance residual error
  vector[N] sigma2_e_mu;                 // mean of var residual error
  vector[N] sigma_e;                     // var residual error
  for (i in 1:J) {                       
                                         // Assigns a different sd to each random intercept
    logsigma2_a_mu[i] = theta_a0 + theta_a1*z[i];
    sigma2_a_mu[i]    = exp(logsigma2_a_mu[i]);
    sigma_a[i]        = sqrt(sigma2_a_mu[i]);
  }
  for (i in 1:N) {                    
                                          // Assigns a different sd to each residual error   
    logsigma2_e_mu[i] = theta_e0 + theta_e1*Z[i];
    sigma2_e_mu[i]    = exp(logsigma2_e_mu[i]);
    sigma_e[i]        = sqrt(sigma2_e_mu[i]);
                                          // Assigns a different predicted mean to ech individual depending of group of belonging
    y_mu[i] = a[G[i]];                  
  }
}
model {
  // Priors 
  theta_a0    ~ cauchy(0,2.5);
  theta_e0    ~ cauchy(0,2.5);
  theta_a1    ~ cauchy(0,2.5);
  theta_e1    ~ cauchy(0,2.5);
  // Variances
  sigma2_a ~ lognormal(sigma2_a_mu,0.25);
  sigma2_e ~ lognormal(sigma2_e_mu,0.25);
  // Random intercept 
  a ~ normal(0, sigma_a);
  // Outcome variable
  y ~ normal(y_mu, sigma_e);
}
'
  1. Fit the model and compare the estimates to the parameters used in the DGP.
mymodel <- stan(model_code=structured_dispersion, model_name="structured_dispersion", 
iter=2000, pars=c("theta_e0", "theta_e1","theta_a0", "theta_a1"), data=data, chains=3)
print(mymodel)
Inference for Stan model: structured_dispersion.
3 chains, each with iter=2000; warmup=1000; thin=1; 
post-warmup draws per chain=1000, total post-warmup draws=3000.

           mean se_mean    sd    2.5%    25%    50%    75%  97.5% n_eff Rhat
theta_e0   5.63    0.06  0.19    5.31   5.46   5.63   5.73   6.04    10 1.17
theta_e1  -2.79    0.02  0.06   -2.91  -2.82  -2.79  -2.75  -2.68    12 1.13
theta_a0  -3.87    0.20  0.46   -4.69  -4.24  -3.83  -3.53  -3.07     5 1.80
theta_a1   1.73    0.06  0.12    1.53   1.64   1.73   1.81   1.96     4 1.96
lp__     -63.59    3.02 23.12 -110.10 -78.88 -63.47 -47.50 -20.06    59 1.04

Samples were drawn using NUTS(diag_e) at Mon Dec  3 17:15:39 2018.
For each parameter, n_eff is a crude measure of effective sample size,
and Rhat is the potential scale reduction factor on split chains (at 
convergence, Rhat=1).
  1. Uset he functionlaunch_shinystan from package rstanarm to get visual and numerical summaries of model parameters and convergence diagnostics.
LS0tCnRpdGxlOiAiQmF5ZXNpYW4gbXVsdGktbGV2ZWwgbW9kZWwgZm9yIGRhdGEgd2l0aCBzdHJ1Y3R1cmVkIGRpc3BlcnNpb24iCm91dHB1dDoKICBodG1sX25vdGVib29rOiBkZWZhdWx0Ci0tLQoKYGBge3Igc2V0dXAsIGluY2x1ZGU9RkFMU0V9CmtuaXRyOjpvcHRzX2NodW5rJHNldChlY2hvID0gVFJVRSkKYGBgCgoKVGhpcyBwb3N0IHByb3ZpZGVzIGNvZGUgdG8gbW9kZWwgdmFyaWFuY2UgY29tcG9uZW50cyBpbiBhIG11bHRpLWxldmVsIHNldHRpbmcuIFRoaXMgaXMgdXNlZnVsIHdoZW4gdmFyaWFuY2VzIGFyZSBoZXRlcm9za2VkYXN0aWMgYW5kLCB3ZSBzdXNwZWN0LCBoZXRlcm9za2VkYXN0aWNpdHkgaXMgZHJpdmVuIGJ5IHNvbWUgb2JzZXJ2ZWQgdmFyaWFibGUocykuCgojIyMjIFN0ZXBzOiAKCjEuIExvYWQgdGhlIGRhdGEuIEZvciB0aGlzIGRlbW9uc3RyYXRpb24gSSB3aWxsIHNpbXVsYXRlIGRhdGEgaW4gd2hpY2ggNjAwIGluZGl2aWR1YWwgKCRpJCkgYXJlIG5lc3RlZCBpbnRvIDIwMCBncm91cHMgKCRqJCkuICRZJCBpcyB0aGUgb3V0Y29tZSB2YXJpYWJsZSBhbmQgSSBhbGxvdyBmb3IgcmFuZG9tIGludGVyY2VwdHMgYXQgdGhlIGdyb3VwIGxldmVsIHBsdXMgc3RydWN0dXJlZCBkaXNwZXJzaW9uIGF0IGJvdGggZ3JvdXAtbGV2ZWwgYW5kIGluZGl2aWR1YWwtbGV2ZWwuIEluIHRoaXMgcGFydGljdWxhciBjYXNlIHRoZSB2YXJpYW5jZSBjb21wb25lbnRzIGFyZSBmdW5jdGlvbnMgb2YgdGhlIHNhbWUgdmFyaWFibGUgJHokLCBidXQgdGhpcyBwcmVkaWN0b3IgaGFzIG9wcG9zaXRlIGVmZmVjdHMgb24gZWFjaCBvZiB0aGVtLgoKTW9yZSBmb3JtYWxseSwgdGhlIERHUCBjYW4gYmUgZGVzY3JpYmVkIGFzOiAkWSA9IFxtYXRoYmZ7MX1fe2t9IFxvdGltZXMgXG1hdGhiZnsxfV97bn1cbXUgKyBcbWF0aGJme0l9X3trfSBcb3RpbWVzIFxtYXRoYmZ7MX1fe259IFxtYXRoYmZ7YX0gKyBcbWF0aGJme0l9X3trfSBcb3RpbWVzIFxtYXRoYmZ7SX1fe259IFxtYXRoYmZ7ZX0kLCB3aGVyZSAkXG1hdGhiZnthfSQgYW5kICRcbWF0aGJme2V9JCBhcmUgcmFuZG9tIGVmZmVjdHMsIHN1Y2ggdGhhdCAkXG1hdGhiZnthfSBcc2ltIE4oXG1hdGhiZnswfSwgXG1hdGhiZntcc2lnbWFfe2F9fSkkIGFuZCAkXG1hdGhiZntlfSBcc2ltIE4oXG1hdGhiZnswfSwgXG1hdGhiZntcc2lnbWFfe2V9fSkkLiBNb3Jlb3ZlciwgYm90aCB2YXJpYW5jZXMgYXJlIGZ1bmN0aW9ucyBvZiAkeiQ6ICRcbWF0aGJme1xzaWdtYV97YX19PWYoXG1hdGhiZnt6fSkkIGFuZCAkXG1hdGhiZntcc2lnbWFfe2V9fT1nKFxtYXRoYmZ7en0pJC4KCgpgYGB7cn0KCm49MyAgICMgTnVtYmVyIG9mIGluZGl2aWR1YWxzIGJ5IGdyb3VwCms9MjAwICMgTnVtYmVyIG9mICBncm91cHMKCiMgRGVzaWduIG1hdHJpY2VzIGZvciBtdWx0aS1sZXZlbHMKCk9uZS5rPW1hdHJpeChyZXAoMSxrKSkKT25lLm49bWF0cml4KHJlcCgxLG4pKQpJLms9ZGlhZyhrKQpJLm49ZGlhZyhuKQptdT0wCgojIHZhcmlhbmNlIHByZWRpY3RvciAKCnogPSBydW5pZihrLDEsNSkgICAgICAgICAgICAgICAjIGluZGVwIHZhcmlhYmxlIGluIHZhcmlhbmNlIGZ1bmN0aW9uIGF0IGdyb3VwIGxldmVsClogPSAoSS5rJXglT25lLm4pJSoleiAgICAgICAgICAjIGluZGVwIHZhcmlhYmxlIGluIHZhcmlhbmNlIGZ1bmN0aW9uIGF0IGluZGl2aWR1YWwgbGV2ZWwgCgoKIyBBbiBhcmJpdHJhcnkgZnVuY3Rpb24gZm9yIHZhcmlhbmNlIGF0IHRoZSBncm91cCBsZXZlbAoKVEhFVEFfYTAgPSAgLTUKVEhFVEFfYTEgID0gMS45CnZhci5hID0gZXhwKFRIRVRBX2EwICsgVEhFVEFfYTEqeiArIHJub3JtKGspKQpzZC5hICA9IHNxcnQodmFyLmEpCgoKIyBBbiBhcmJpdHJhcnkgZnVuY3Rpb24gZm9yIHZhcmlhbmNlIGF0IGluZGl2aWR1YWwgbGV2ZWwgKHJlc2lkdWFsIHZhcmlhbmNlKQoKVEhFVEFfZTAgPSAgNQpUSEVUQV9lMSAgPSAtMi43CnZhci5lID0gZXhwKFRIRVRBX2UwICsgVEhFVEFfZTEqWiArIHJub3JtKGsqbikpCnNkLmUgID0gc3FydCh2YXIuZSkKCiMgUmFuZG9tIGludGVyY2VwdHMKYT1ybm9ybShrLDAsc2QuYSkKCiMgUmFuZG9tIGVycm9yIAplPXJub3JtKGsqbiwwLHNkLmUpIAoKCiMgQ3JlYXRlcyBZID0gbXUgKyByYW5kb20gaW50ZXJjZXB0ICsgcmFuZG9tIGVycm9yCgpZID0gKE9uZS5rJXglT25lLm4pKm11ICsgKEkuayV4JU9uZS5uKSUqJWEgKyAoSS5rJXglSS5uKSUqJWUgICAKCiMgR2VuZXJhdGUgaW5kaWNhdG9ycy4gVG8gYmUgdXNlZCBsYXRlci4gCmdyb3VwPW1hdHJpeChyZXAoMTprKSkgICMgdmVjdG9yIHdpdGggZ3JvdXAgaW5kaWNhdG9yIGZvciBlYWNoIGdyb3VwCkdyb3VwPShJLmsleCVPbmUubiklKiVncm91cCAjIHZlY3RvciB3aXRoIEdyb3VwIGluZGljYXRvciBmb3IgZWFjaCBpbmRpdmlkdWFsCmBgYAoKCkEgc2ltcGxlIHNjYXR0ZXJwbG90IG9mICRZJCBhbmQgJHokIHNob3VsZCByZXZlYWwgdmFyaWFuY2Ugc3RydWN0dXJlLiAKCgpgYGB7cixlY2hvPUZBTFNFLG1lc3NhZ2U9RkFMU0V9CmxpYnJhcnkoInRpZHl2ZXJzZSIpCgpkYXRhIDwtIGRhdGEuZnJhbWUoeT1ZLHo9WikKCnBsb3QgPC0gZGF0YSAlPiUgZ2dwbG90KGFlcyh4PVoseT1ZKSkgKyBnZW9tX3BvaW50KGFscGhhPTAuMzMsIGNvbG91cj0iYmx1ZSIpICsgCgkJeWxpbSgtNS41LDUuNSkgKyAKCQlsYWJzKHg9IloiLHk9IlkiKQpwcmludChwbG90KQpgYGAKCgoyLiBMb2FkIGBgYHJzdGFuYGBgIHRvIHBlcmZvcm0gQmF5ZXNpYW4gZXN0aW1hdGlvbiB2aWEgSGFsbWlsdG9uaWFuIE1vbnRlIENhcmxvLiBQdXQgdGhlIGRhdGEgaW50byBhIGxpc3QuCgoKYGBge3IsbWVzc2FnZT1GQUxTRX0KCmxpYnJhcnkoInJzdGFuIikKCkRhdGFfbDEgPC0gZGF0YS5mcmFtZShZLEdyb3VwLFopICMgZGF0YSBpbmRpdmlkdWFsLWxldmVsCkRhdGFfbDIgPC0gZGF0YS5mcmFtZShncm91cCx6KSAgICMgZGF0YSBncm91cC1sZXZlbCAKZGF0YSAgICA8LSBsaXN0KE49biprLEo9ayx5PURhdGFfbDEkWSxHPURhdGFfbDEkR3JvdXAsWj1EYXRhX2wxJFosIGc9RGF0YV9sMiRncm91cCx6PURhdGFfbDIkeikKCmBgYAoKCjMuIFdyaXRlIHRoZSBtb2RlbCBpbiBgYGBzdGFuYGBgLgoKYGBge3IsIG1lc3NhZ2U9RkFMU0V9CgpzdHJ1Y3R1cmVkX2Rpc3BlcnNpb24gPC0gJwoKZGF0YSB7CiAgaW50PGxvd2VyPTA+IEo7ICAgICAgICAgICAgICAgICAgIC8vIG51bWJlciBvZiBncm91cHMgCiAgaW50PGxvd2VyPTA+IE47ICAgICAgICAgICAgICAgICAgIC8vIG51bWJlciBvZiBpbmRpdmlkdWFscwogIGludDxsb3dlcj0xLCB1cHBlcj1KPiAgR1tOXTsgICAgICAvLyBtYXAgaW5kaXZpZHVhbHMgdG8gZ3JvdXBzCiAgdmVjdG9yW05dIHk7ICAgICAgICAgICAgICAgICAgICAgIC8vIG91dGNvbWUgdmFyaWFibGUKICB2ZWN0b3JbSl0gejsgICAgICAgICAgICAgICAgICAgICAgLy8gZ3JvdXAgbGV2ZWwgZGlzcGVyc2lvbiBwcmVkaWN0b3IKICB2ZWN0b3JbTl0gWjsgICAgICAgICAgICAgICAgICAgICAgLy8gaW5kaXZpZHVhbCBsZXZlbCBkaXNwZXJzaW9uIHByZWRpY3Rvcgp9CgoKcGFyYW1ldGVycyB7CgogIHZlY3RvcltKXSBhOyAgICAgICAgICAgICAgICAgICAgICAgICAgIC8vIFJhbmRvbSBJbnRlcmNlcHQgICAgICAgICAgIAogIAogIHJlYWwgdGhldGFfYTA7ICAgICAgICAgICAgICAgICAgICAgICAgIC8vIEludGVyY2VwdCBtb2RlbCBmb3IgdmFyaWFuY2Ugb2YgcmFuZG9tIGludGVyY2VwdAogIHJlYWwgdGhldGFfZTA7ICAgICAgICAgICAgICAgICAgICAgICAgIC8vIEludGVyY2VwdCBtb2RlbCBmb3IgdmFyaWFuY2Ugb2YgcmVzaWR1YWwgZXJyb3IKICAKICByZWFsIHRoZXRhX2ExOyAgICAgICAgICAgICAgICAgICAgICAgICAvLyBTbG9wZSBtb2RlbCBmb3IgdmFyaWFuY2Ugb2YgcmFuZG9tIGludGVyY2VwdAogIHJlYWwgdGhldGFfZTE7ICAgICAgICAgICAgICAgICAgICAgICAgIC8vIFNsb3BlIG1vZGVsIGZvciB2YXJpYW5jZSBvZiByZXNpZHVhbCBlcnJvcgoKICB2ZWN0b3I8bG93ZXI9MD5bSl0gc2lnbWEyX2E7ICAgICAgICAgICAvLyBoZXRlcm9nZW5lb3VzIHZhciBmb3IgcmFuZG9tIGludGVyY2VwdCAKCiAgdmVjdG9yPGxvd2VyPTA+W05dIHNpZ21hMl9lOyAgICAgICAgICAgLy8gaGV0ZXJvZ2VuZW91cyB2YXIgcmVzaWR1YWwgZXJyb3IKCn0KCgp0cmFuc2Zvcm1lZCBwYXJhbWV0ZXJzIHsKICAKICB2ZWN0b3JbTl0geV9tdTsgICAgICAgICAgICAgICAgICAgICAgICAvLyBSYW5kb20gZWZmZWN0cwoKICB2ZWN0b3JbSl0gbG9nc2lnbWEyX2FfbXU7ICAgICAgICAgICAgICAvLyBtZWFuIG9mIGxvZy12YXJpYW5jZSBmb3IgcmFuZG9tIGludGVyY2VwdCAKICB2ZWN0b3JbSl0gc2lnbWEyX2FfbXU7ICAgICAgICAgICAgICAgICAvLyBtZWFuIG9mIHZhcmlhbmNlIGZvciByYW5kb20gaW50ZXJjZXB0IAogIHZlY3RvcltKXSBzaWdtYV9hOyAgICAgICAgICAgICAgICAgICAgIC8vIHZhciBmb3IgcmFuZG9tIGludGVyY2VwdCAKCiAgdmVjdG9yW05dIGxvZ3NpZ21hMl9lX211OyAgICAgICAgICAgICAgLy8gbWVhbiBvZiBsb2ctdmFyaWFuY2UgcmVzaWR1YWwgZXJyb3IKICB2ZWN0b3JbTl0gc2lnbWEyX2VfbXU7ICAgICAgICAgICAgICAgICAvLyBtZWFuIG9mIHZhciByZXNpZHVhbCBlcnJvcgogIHZlY3RvcltOXSBzaWdtYV9lOyAgICAgICAgICAgICAgICAgICAgIC8vIHZhciByZXNpZHVhbCBlcnJvcgoKCiAgZm9yIChpIGluIDE6SikgeyAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLy8gQXNzaWducyBhIGRpZmZlcmVudCBzZCB0byBlYWNoIHJhbmRvbSBpbnRlcmNlcHQKICAgIGxvZ3NpZ21hMl9hX211W2ldID0gdGhldGFfYTAgKyB0aGV0YV9hMSp6W2ldOwogICAgc2lnbWEyX2FfbXVbaV0gICAgPSBleHAobG9nc2lnbWEyX2FfbXVbaV0pOwogICAgc2lnbWFfYVtpXSAgICAgICAgPSBzcXJ0KHNpZ21hMl9hX211W2ldKTsKICB9CgogIGZvciAoaSBpbiAxOk4pIHsgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAvLyBBc3NpZ25zIGEgZGlmZmVyZW50IHNkIHRvIGVhY2ggcmVzaWR1YWwgZXJyb3IgICAKICAgIGxvZ3NpZ21hMl9lX211W2ldID0gdGhldGFfZTAgKyB0aGV0YV9lMSpaW2ldOwogICAgc2lnbWEyX2VfbXVbaV0gICAgPSBleHAobG9nc2lnbWEyX2VfbXVbaV0pOwogICAgc2lnbWFfZVtpXSAgICAgICAgPSBzcXJ0KHNpZ21hMl9lX211W2ldKTsKCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC8vIEFzc2lnbnMgYSBkaWZmZXJlbnQgcHJlZGljdGVkIG1lYW4gdG8gZWNoIGluZGl2aWR1YWwgZGVwZW5kaW5nIG9mIGdyb3VwIG9mIGJlbG9uZ2luZwogICAgeV9tdVtpXSA9IGFbR1tpXV07ICAgICAgICAgICAgICAgICAgCiAgfQoKCn0KCm1vZGVsIHsKCiAgLy8gUHJpb3JzIAoKICB0aGV0YV9hMCAgICB+IGNhdWNoeSgwLDIuNSk7CiAgdGhldGFfZTAgICAgfiBjYXVjaHkoMCwyLjUpOwogIHRoZXRhX2ExICAgIH4gY2F1Y2h5KDAsMi41KTsKICB0aGV0YV9lMSAgICB+IGNhdWNoeSgwLDIuNSk7CgogIC8vIFZhcmlhbmNlcwogIHNpZ21hMl9hIH4gbG9nbm9ybWFsKHNpZ21hMl9hX211LDAuMjUpOwogIHNpZ21hMl9lIH4gbG9nbm9ybWFsKHNpZ21hMl9lX211LDAuMjUpOwoKICAvLyBSYW5kb20gaW50ZXJjZXB0IAogIGEgfiBub3JtYWwoMCwgc2lnbWFfYSk7CgogIC8vIE91dGNvbWUgdmFyaWFibGUKICB5IH4gbm9ybWFsKHlfbXUsIHNpZ21hX2UpOwoKfQonCgpgYGAKCgo0LiBGaXQgdGhlIG1vZGVsIGFuZCBjb21wYXJlIHRoZSBlc3RpbWF0ZXMgdG8gdGhlIHBhcmFtZXRlcnMgdXNlZCBpbiB0aGUgREdQLiAKCmBgYHtyLCBtZXNzYWdlPUZBTFNFLCByZXN1bHRzPSJoaWRlIn0KbXltb2RlbCA8LSBzdGFuKG1vZGVsX2NvZGU9c3RydWN0dXJlZF9kaXNwZXJzaW9uLCBtb2RlbF9uYW1lPSJzdHJ1Y3R1cmVkX2Rpc3BlcnNpb24iLCAKaXRlcj0yMDAwLCBwYXJzPWMoInRoZXRhX2UwIiwgInRoZXRhX2UxIiwidGhldGFfYTAiLCAidGhldGFfYTEiKSwgZGF0YT1kYXRhLCBjaGFpbnM9MykKCmBgYAoKYGBge3J9CnByaW50KG15bW9kZWwpCmBgYAoKCjUuIFVzZXQgaGUgZnVuY3Rpb25gYGBsYXVuY2hfc2hpbnlzdGFuYGBgIGZyb20gcGFja2FnZSBgYGByc3RhbmFybWBgYCB0byBnZXQgdmlzdWFsIGFuZCBudW1lcmljYWwgc3VtbWFyaWVzIG9mIG1vZGVsIHBhcmFtZXRlcnMgYW5kIGNvbnZlcmdlbmNlIGRpYWdub3N0aWNzLgoKCgoKCgo=