Goals:

  • Illustrate why naive implementations of model selection using Lasso are problematic


Data generating process

We consider a DGP with correlated variables:

  • \(p\geq10\) covariates drawn from a multivariate normal distribution: \(X \sim N(0,\Sigma)\), where we set in \(\Sigma\) the diagonal entries (variance) to one and the off-diagonal entries (covariance) to 0.5.

  • The outcome model is \(Y = \underbrace{\beta_0 + \beta_1 X_1 + ... + \beta_{10} X_{10}}_{\text{CEF }f(X)} + e\), where \(X_j\) is the \(j\)-th column of \(X\) and \(e \sim N(0,2)\)

  • We consider the following parameters \(\beta_0 = 0\), \(\beta_1 = 1\), \(\beta_2 = 0.9\), …, \(\beta_9 = 0.2\), \(\beta_{10} = 0.1\) such that the first 10 variables have a decreasing impact on the outcome and any further variables are just irrelevant noise with no true predictive power

We set \(p=99\) and work with sample size \(N=100\).

# Load the packages required for later
library(tidyverse)
library(mvtnorm)
library(glmnet)

set.seed(1234) # For replicability

# Define the relevant parameters
n = 100
var = 1
cov = 0.5
p = 99

beta = c(0,seq(1,0.1,-0.1),rep(0,p-10))
sig = matrix(cov,p,p)
diag(sig) = var


A causal question and different advice

You are interested in the causal effect of variable \(X_1\) on outcome \(Y\). You know that you should control for additional variables, but not really which specification to choose. You organize a meeting with some colleagues and ask for advice. You receive three different answers:

  • Professor: Looks in a coffee cup and tells you that it is obvious that you should control for \(X_2-X_{10}\) using OLS and leaves.

The remaining PhD students just finished a course in “Big Data, Machine Learning and AI” and are very excited about variable selection via Lasso because this is exactly what you are asking for:

  • PhD student 1: Tells you that you should just throw everything in a Lasso regression and report the resulting coefficient of \(\beta_1\) as your causal effect.

  • PhD student 2: Is skeptical because the causal effect is shrunken towards zero and proposes to leave the coefficient of \(\beta_1\) unpenalized and to only penalize the other variables.


A simulation study to figure out what works

We run a simulation study that draws 1000 random samples based on our DGP and stores the estimates resulting from the three proposed procedures (runs several minutes).

repl = 1000

beta1_ols = beta1_lasso = beta1_lasso_unpen = rep(NA,repl)

for (i in 1:repl) {
  x = cbind(rep(1,n),rmvnorm(n,sigma=sig))
  y = x %*% beta + rnorm(n,0,2)
  
  # OLS
  beta1_ols[i] = lm(y~x[,2:11])$coefficients[2]
  
  # Plain Lasso (specifying lambda.min.ratio speeds up)
  cv_lasso = cv.glmnet(x[,-1],y,lambda.min.ratio=0.001) 
  lasso_temp = glmnet(x[,-1],y,lambda=cv_lasso$lambda.min)
  beta1_lasso[i] = lasso_temp$beta[1]
  
  # Lasso with unpenalized X_1
  cv_lasso = cv.glmnet(x[,-1],y,penalty.factor=c(0,rep(1,p-1)),lambda.min.ratio=0.001)
  lasso_temp = glmnet(x[,-1],y,lambda=cv_lasso$lambda.min,penalty.factor=c(0,rep(1,p-1)))
  beta1_lasso_unpen[i] = lasso_temp$beta[1]
}


Let’s check the resulting estimator distributions of the proposed strategies:

Professor OLS

Does it work?

Yes! The distribution of the estimator has a mean (dashed line) close to the true value of one (solid line) \(\Rightarrow\) unbiased:

df = data.frame(x=beta1_ols)
ggplot(df,aes(x=x)) + geom_histogram(bins=30,aes(y =..density..)) + 
              geom_vline(xintercept = c(beta[2],mean(df$x)),linetype=c('solid','dashed'))

Why? (i) The model was chosen without looking at the data \(\Rightarrow\) no problems with post-selection issues. (ii) The correct model was chosen (however the Prof. managed to do this). (iii) OLS is used, which is the best unbiased estimator for such a setting.


Student 1’s plain Lasso

Does it work?

No! The estimator is clearly downward biased.

df = data.frame(x=beta1_lasso)
ggplot(df,aes(x=x)) + geom_histogram(bins=30,aes(y =..density..)) + 
  geom_vline(xintercept = c(beta[2],mean(df$x)),linetype=c('solid','dashed'))

Why? Student 2 is right. The penalization shrinks the parameter of interest towards zero. In several replications the variable of interest is not even selected, which explains the spike at zero:

paste("Share of replications where variable of interest not selected:",round( sum(beta1_lasso == 0)) / repl *100,"%")
[1] "Share of replications where variable of interest not selected: 2.8 %"


Student 2’s Lasso with unpenalized parameter of interest

Does it work?

No! The estimator is upward biased:

df = data.frame(x=beta1_lasso_unpen)
ggplot(df,aes(x=x)) + geom_histogram(bins=30,aes(y =..density..)) + 
  geom_vline(xintercept = c(beta[2],mean(df$x)),linetype=c('solid','dashed'))

Why? Lasso is not aware of the causal problem at hand. Thus, it could use the omitted variable bias (OVB) for prediction purposes. Correcting OVB requires to build up regression coefficients of confounding variables, but this is costly because of the penalization. Leveraging the OVB in the unpenalized coefficient is “for free”. On the other hand, this must not be the case. With a sufficiently low penalty term, it could work. But plain Lasso does not care about an unbiased parameter \(\beta_1\).

To understand this better, let’s zoom into the coefficient path of the last replication:

cv_lasso_unpen = cv.glmnet(x[,-1],y,penalty.factor=c(0,rep(1,p-1)),lambda.min.ratio=0.002)
plot(cv_lasso_unpen)


lasso_unpen = glmnet(x[,-1],y,penalty.factor=c(0,rep(1,p-1)),lambda.min.ratio=0.002)
plot(lasso_unpen,xvar = "lambda",label=T)

The empty model consists now of the unpenalized variable and it leverages the full omitted variable bias for prediction. The OVB is reduced as the other variables receive at least parts of their coefficients. In principle, there would be a sweet spot on the trajectory of \(\beta_1\), but cross-validation seems to systematically favor models where the OVB in \(\beta_1\) is used for prediction.

Such things happen if the ML method is not “taught” that we are interest in a causal effect in the correct way.



Take-away

  • If somebody tells us the correct model without looking at the data, life is good.

  • In the likely case that not, we need to be careful how we leverage the ML machinery for causal analysis.

  • There are a lot of interesting things to be learned about how to teach ML that we are estimating causal effects.



Suggestions to play with the toy model

Feel free to play around with the code. This is useful to sharpen and challenge your understanding of the methods. Think about the consequences of a modifications before you run it and check whether the results are in line with your expectation. Some suggestions:

  • Modify DGP (correlation of covariates, betas, noise term, …)

  • Increase the number of observations

  • Implement other ad hoc ideas that one could come up with

LS0tDQp0aXRsZTogIkNhdXNhbCBNTDogV2h5IG5haXZlIG1vZGVsIHNlbGVjdGlvbiBmYWlscyINCnN1YnRpdGxlOiAiU2ltdWxhdGlvbiBub3RlYm9vayINCmF1dGhvcjogIk1pY2hhZWwgS25hdXMiDQpkYXRlOiAiMDIvMjMiDQpvdXRwdXQ6IA0KICBodG1sX25vdGVib29rOg0KICAgIHRvYzogdHJ1ZQ0KICAgIHRvY19mbG9hdDogdHJ1ZQ0KICAgIGNvZGVfZm9sZGluZzogc2hvdw0KLS0tDQoNCg0KR29hbHM6DQoNCi0gSWxsdXN0cmF0ZSB3aHkgbmFpdmUgaW1wbGVtZW50YXRpb25zIG9mIG1vZGVsIHNlbGVjdGlvbiB1c2luZyBMYXNzbyBhcmUgcHJvYmxlbWF0aWMNCg0KPGJyPg0KDQoNCiMgRGF0YSBnZW5lcmF0aW5nIHByb2Nlc3MNCg0KV2UgY29uc2lkZXIgYSBER1Agd2l0aCBjb3JyZWxhdGVkIHZhcmlhYmxlczoNCg0KLSAkcFxnZXExMCQgY292YXJpYXRlcyBkcmF3biBmcm9tIGEgbXVsdGl2YXJpYXRlIG5vcm1hbCBkaXN0cmlidXRpb246ICRYIFxzaW0gTigwLFxTaWdtYSkkLCB3aGVyZSB3ZSBzZXQgaW4gJFxTaWdtYSQgdGhlIGRpYWdvbmFsIGVudHJpZXMgKHZhcmlhbmNlKSB0byBvbmUgYW5kIHRoZSBvZmYtZGlhZ29uYWwgZW50cmllcyAoY292YXJpYW5jZSkgdG8gMC41Lg0KDQotIFRoZSBvdXRjb21lIG1vZGVsIGlzICRZID0gXHVuZGVyYnJhY2V7XGJldGFfMCArIFxiZXRhXzEgWF8xICsgLi4uICsgXGJldGFfezEwfSBYX3sxMH19X3tcdGV4dHtDRUYgfWYoWCl9ICsgZSQsIHdoZXJlICRYX2okIGlzIHRoZSAkaiQtdGggY29sdW1uIG9mICRYJCBhbmQgJGUgXHNpbSBOKDAsMikkDQoNCi0gV2UgY29uc2lkZXIgdGhlIGZvbGxvd2luZyBwYXJhbWV0ZXJzICRcYmV0YV8wID0gMCQsICRcYmV0YV8xID0gMSQsICRcYmV0YV8yID0gMC45JCwgLi4uLCAkXGJldGFfOSA9IDAuMiQsICRcYmV0YV97MTB9ID0gMC4xJCBzdWNoIHRoYXQgdGhlIGZpcnN0IDEwIHZhcmlhYmxlcyBoYXZlIGEgZGVjcmVhc2luZyBpbXBhY3Qgb24gdGhlIG91dGNvbWUgYW5kIGFueSBmdXJ0aGVyIHZhcmlhYmxlcyBhcmUganVzdCBpcnJlbGV2YW50IG5vaXNlIHdpdGggbm8gdHJ1ZSBwcmVkaWN0aXZlIHBvd2VyDQoNCldlIHNldCAkcD05OSQgYW5kIHdvcmsgd2l0aCBzYW1wbGUgc2l6ZSAkTj0xMDAkLg0KDQpgYGB7ciwgd2FybmluZyA9IEYsIG1lc3NhZ2UgPSBGfQ0KIyBMb2FkIHRoZSBwYWNrYWdlcyByZXF1aXJlZCBmb3IgbGF0ZXINCmxpYnJhcnkodGlkeXZlcnNlKQ0KbGlicmFyeShtdnRub3JtKQ0KbGlicmFyeShnbG1uZXQpDQoNCnNldC5zZWVkKDEyMzQpICMgRm9yIHJlcGxpY2FiaWxpdHkNCg0KIyBEZWZpbmUgdGhlIHJlbGV2YW50IHBhcmFtZXRlcnMNCm4gPSAxMDANCnZhciA9IDENCmNvdiA9IDAuNQ0KcCA9IDk5DQoNCmJldGEgPSBjKDAsc2VxKDEsMC4xLC0wLjEpLHJlcCgwLHAtMTApKQ0Kc2lnID0gbWF0cml4KGNvdixwLHApDQpkaWFnKHNpZykgPSB2YXINCmBgYA0KDQo8YnI+DQoNCg0KIyBBIGNhdXNhbCBxdWVzdGlvbiBhbmQgZGlmZmVyZW50IGFkdmljZQ0KDQpZb3UgYXJlIGludGVyZXN0ZWQgaW4gdGhlIGNhdXNhbCBlZmZlY3Qgb2YgdmFyaWFibGUgJFhfMSQgb24gb3V0Y29tZSAkWSQuIFlvdSBrbm93IHRoYXQgeW91IHNob3VsZCBjb250cm9sIGZvciBhZGRpdGlvbmFsIHZhcmlhYmxlcywgYnV0IG5vdCByZWFsbHkgd2hpY2ggc3BlY2lmaWNhdGlvbiB0byBjaG9vc2UuIFlvdSBvcmdhbml6ZSBhIG1lZXRpbmcgd2l0aCBzb21lIGNvbGxlYWd1ZXMgYW5kIGFzayBmb3IgYWR2aWNlLiBZb3UgcmVjZWl2ZSB0aHJlZSBkaWZmZXJlbnQgYW5zd2VyczoNCg0KLSBQcm9mZXNzb3I6IExvb2tzIGluIGEgY29mZmVlIGN1cCBhbmQgdGVsbHMgeW91IHRoYXQgaXQgaXMgb2J2aW91cyB0aGF0IHlvdSBzaG91bGQgY29udHJvbCBmb3IgJFhfMi1YX3sxMH0kIHVzaW5nIE9MUyBhbmQgbGVhdmVzLg0KDQpUaGUgcmVtYWluaW5nIFBoRCBzdHVkZW50cyBqdXN0IGZpbmlzaGVkIGEgY291cnNlIGluICJCaWcgRGF0YSwgTWFjaGluZSBMZWFybmluZyBhbmQgQUkiIGFuZCBhcmUgdmVyeSBleGNpdGVkIGFib3V0IHZhcmlhYmxlIHNlbGVjdGlvbiB2aWEgTGFzc28gYmVjYXVzZSB0aGlzIGlzIGV4YWN0bHkgd2hhdCB5b3UgYXJlIGFza2luZyBmb3I6DQoNCi0gUGhEIHN0dWRlbnQgMTogVGVsbHMgeW91IHRoYXQgeW91IHNob3VsZCBqdXN0IHRocm93IGV2ZXJ5dGhpbmcgaW4gYSBMYXNzbyByZWdyZXNzaW9uIGFuZCByZXBvcnQgdGhlIHJlc3VsdGluZyBjb2VmZmljaWVudCBvZiAkXGJldGFfMSQgYXMgeW91ciBjYXVzYWwgZWZmZWN0Lg0KDQotIFBoRCBzdHVkZW50IDI6IElzIHNrZXB0aWNhbCBiZWNhdXNlIHRoZSBjYXVzYWwgZWZmZWN0IGlzIHNocnVua2VuIHRvd2FyZHMgemVybyBhbmQgcHJvcG9zZXMgdG8gbGVhdmUgdGhlIGNvZWZmaWNpZW50IG9mICRcYmV0YV8xJCB1bnBlbmFsaXplZCBhbmQgdG8gb25seSBwZW5hbGl6ZSB0aGUgb3RoZXIgdmFyaWFibGVzLg0KDQo8YnI+DQoNCiMgQSBzaW11bGF0aW9uIHN0dWR5IHRvIGZpZ3VyZSBvdXQgd2hhdCB3b3Jrcw0KDQpXZSBydW4gYSBzaW11bGF0aW9uIHN0dWR5IHRoYXQgZHJhd3MgMTAwMCByYW5kb20gc2FtcGxlcyBiYXNlZCBvbiBvdXIgREdQIGFuZCBzdG9yZXMgdGhlIGVzdGltYXRlcyByZXN1bHRpbmcgZnJvbSB0aGUgdGhyZWUgcHJvcG9zZWQgcHJvY2VkdXJlcyAocnVucyBzZXZlcmFsIG1pbnV0ZXMpLg0KDQpgYGB7cn0NCnJlcGwgPSAxMDAwDQoNCmJldGExX29scyA9IGJldGExX2xhc3NvID0gYmV0YTFfbGFzc29fdW5wZW4gPSByZXAoTkEscmVwbCkNCg0KZm9yIChpIGluIDE6cmVwbCkgew0KICB4ID0gY2JpbmQocmVwKDEsbikscm12bm9ybShuLHNpZ21hPXNpZykpDQogIHkgPSB4ICUqJSBiZXRhICsgcm5vcm0obiwwLDIpDQogIA0KICAjIE9MUw0KICBiZXRhMV9vbHNbaV0gPSBsbSh5fnhbLDI6MTFdKSRjb2VmZmljaWVudHNbMl0NCiAgDQogICMgUGxhaW4gTGFzc28gKHNwZWNpZnlpbmcgbGFtYmRhLm1pbi5yYXRpbyBzcGVlZHMgdXApDQogIGN2X2xhc3NvID0gY3YuZ2xtbmV0KHhbLC0xXSx5LGxhbWJkYS5taW4ucmF0aW89MC4wMDEpIA0KICBsYXNzb190ZW1wID0gZ2xtbmV0KHhbLC0xXSx5LGxhbWJkYT1jdl9sYXNzbyRsYW1iZGEubWluKQ0KICBiZXRhMV9sYXNzb1tpXSA9IGxhc3NvX3RlbXAkYmV0YVsxXQ0KICANCiAgIyBMYXNzbyB3aXRoIHVucGVuYWxpemVkIFhfMQ0KICBjdl9sYXNzbyA9IGN2LmdsbW5ldCh4WywtMV0seSxwZW5hbHR5LmZhY3Rvcj1jKDAscmVwKDEscC0xKSksbGFtYmRhLm1pbi5yYXRpbz0wLjAwMSkNCiAgbGFzc29fdGVtcCA9IGdsbW5ldCh4WywtMV0seSxsYW1iZGE9Y3ZfbGFzc28kbGFtYmRhLm1pbixwZW5hbHR5LmZhY3Rvcj1jKDAscmVwKDEscC0xKSkpDQogIGJldGExX2xhc3NvX3VucGVuW2ldID0gbGFzc29fdGVtcCRiZXRhWzFdDQp9DQpgYGANCg0KPGJyPg0KDQpMZXQncyBjaGVjayB0aGUgcmVzdWx0aW5nIGVzdGltYXRvciBkaXN0cmlidXRpb25zIG9mIHRoZSBwcm9wb3NlZCBzdHJhdGVnaWVzOg0KDQojIyBQcm9mZXNzb3IgT0xTDQoNCkRvZXMgaXQgd29yaz8gDQoNClllcyEgVGhlIGRpc3RyaWJ1dGlvbiBvZiB0aGUgZXN0aW1hdG9yIGhhcyBhIG1lYW4gKGRhc2hlZCBsaW5lKSBjbG9zZSB0byB0aGUgdHJ1ZSB2YWx1ZSBvZiBvbmUgKHNvbGlkIGxpbmUpICRcUmlnaHRhcnJvdyQgdW5iaWFzZWQ6DQoNCg0KYGBge3J9DQpkZiA9IGRhdGEuZnJhbWUoeD1iZXRhMV9vbHMpDQpnZ3Bsb3QoZGYsYWVzKHg9eCkpICsgZ2VvbV9oaXN0b2dyYW0oYmlucz0zMCxhZXMoeSA9Li5kZW5zaXR5Li4pKSArIA0KICAgICAgICAgICAgICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQgPSBjKGJldGFbMl0sbWVhbihkZiR4KSksbGluZXR5cGU9Yygnc29saWQnLCdkYXNoZWQnKSkNCmBgYA0KDQpXaHk/IChpKSBUaGUgbW9kZWwgd2FzIGNob3NlbiB3aXRob3V0IGxvb2tpbmcgYXQgdGhlIGRhdGEgJFxSaWdodGFycm93JCBubyBwcm9ibGVtcyB3aXRoIHBvc3Qtc2VsZWN0aW9uIGlzc3Vlcy4gKGlpKSBUaGUgY29ycmVjdCBtb2RlbCB3YXMgY2hvc2VuIChob3dldmVyIHRoZSBQcm9mLiBtYW5hZ2VkIHRvIGRvIHRoaXMpLiAoaWlpKSBPTFMgaXMgdXNlZCwgd2hpY2ggaXMgdGhlIGJlc3QgdW5iaWFzZWQgZXN0aW1hdG9yIGZvciBzdWNoIGEgc2V0dGluZy4NCg0KPGJyPg0KDQojIyBTdHVkZW50IDEncyBwbGFpbiBMYXNzbw0KDQpEb2VzIGl0IHdvcms/IA0KDQpObyEgVGhlIGVzdGltYXRvciBpcyBjbGVhcmx5IGRvd253YXJkIGJpYXNlZC4NCg0KYGBge3J9DQpkZiA9IGRhdGEuZnJhbWUoeD1iZXRhMV9sYXNzbykNCmdncGxvdChkZixhZXMoeD14KSkgKyBnZW9tX2hpc3RvZ3JhbShiaW5zPTMwLGFlcyh5ID0uLmRlbnNpdHkuLikpICsgDQogIGdlb21fdmxpbmUoeGludGVyY2VwdCA9IGMoYmV0YVsyXSxtZWFuKGRmJHgpKSxsaW5ldHlwZT1jKCdzb2xpZCcsJ2Rhc2hlZCcpKQ0KYGBgDQpXaHk/IFN0dWRlbnQgMiBpcyByaWdodC4gVGhlIHBlbmFsaXphdGlvbiBzaHJpbmtzIHRoZSBwYXJhbWV0ZXIgb2YgaW50ZXJlc3QgdG93YXJkcyB6ZXJvLiBJbiBzZXZlcmFsIHJlcGxpY2F0aW9ucyB0aGUgdmFyaWFibGUgb2YgaW50ZXJlc3QgaXMgbm90IGV2ZW4gc2VsZWN0ZWQsIHdoaWNoIGV4cGxhaW5zIHRoZSBzcGlrZSBhdCB6ZXJvOg0KDQpgYGB7cn0NCnBhc3RlKCJTaGFyZSBvZiByZXBsaWNhdGlvbnMgd2hlcmUgdmFyaWFibGUgb2YgaW50ZXJlc3Qgbm90IHNlbGVjdGVkOiIscm91bmQoIHN1bShiZXRhMV9sYXNzbyA9PSAwKSkgLyByZXBsICoxMDAsIiUiKQ0KYGBgDQoNCjxicj4NCg0KIyMgU3R1ZGVudCAyJ3MgTGFzc28gd2l0aCB1bnBlbmFsaXplZCBwYXJhbWV0ZXIgb2YgaW50ZXJlc3QNCg0KRG9lcyBpdCB3b3JrPyANCg0KTm8hIFRoZSBlc3RpbWF0b3IgaXMgdXB3YXJkIGJpYXNlZDoNCg0KYGBge3J9DQpkZiA9IGRhdGEuZnJhbWUoeD1iZXRhMV9sYXNzb191bnBlbikNCmdncGxvdChkZixhZXMoeD14KSkgKyBnZW9tX2hpc3RvZ3JhbShiaW5zPTMwLGFlcyh5ID0uLmRlbnNpdHkuLikpICsgDQogIGdlb21fdmxpbmUoeGludGVyY2VwdCA9IGMoYmV0YVsyXSxtZWFuKGRmJHgpKSxsaW5ldHlwZT1jKCdzb2xpZCcsJ2Rhc2hlZCcpKQ0KYGBgDQpXaHk/IExhc3NvIGlzIG5vdCBhd2FyZSBvZiB0aGUgY2F1c2FsIHByb2JsZW0gYXQgaGFuZC4gVGh1cywgaXQgY291bGQgdXNlIHRoZSBvbWl0dGVkIHZhcmlhYmxlIGJpYXMgKE9WQikgZm9yIHByZWRpY3Rpb24gcHVycG9zZXMuIENvcnJlY3RpbmcgT1ZCIHJlcXVpcmVzIHRvIGJ1aWxkIHVwIHJlZ3Jlc3Npb24gY29lZmZpY2llbnRzIG9mIGNvbmZvdW5kaW5nIHZhcmlhYmxlcywgYnV0IHRoaXMgaXMgY29zdGx5IGJlY2F1c2Ugb2YgdGhlIHBlbmFsaXphdGlvbi4gTGV2ZXJhZ2luZyB0aGUgT1ZCIGluIHRoZSB1bnBlbmFsaXplZCBjb2VmZmljaWVudCBpcyAiZm9yIGZyZWUiLiBPbiB0aGUgb3RoZXIgaGFuZCwgdGhpcyBtdXN0IG5vdCBiZSB0aGUgY2FzZS4gV2l0aCBhIHN1ZmZpY2llbnRseSBsb3cgcGVuYWx0eSB0ZXJtLCBpdCBjb3VsZCB3b3JrLiBCdXQgcGxhaW4gTGFzc28gZG9lcyBub3QgY2FyZSBhYm91dCBhbiB1bmJpYXNlZCBwYXJhbWV0ZXIgJFxiZXRhXzEkLg0KDQpUbyB1bmRlcnN0YW5kIHRoaXMgYmV0dGVyLCBsZXQncyB6b29tIGludG8gdGhlIGNvZWZmaWNpZW50IHBhdGggb2YgdGhlIGxhc3QgcmVwbGljYXRpb246DQoNCmBgYHtyfQ0KY3ZfbGFzc29fdW5wZW4gPSBjdi5nbG1uZXQoeFssLTFdLHkscGVuYWx0eS5mYWN0b3I9YygwLHJlcCgxLHAtMSkpLGxhbWJkYS5taW4ucmF0aW89MC4wMDIpDQpwbG90KGN2X2xhc3NvX3VucGVuKQ0KDQpsYXNzb191bnBlbiA9IGdsbW5ldCh4WywtMV0seSxwZW5hbHR5LmZhY3Rvcj1jKDAscmVwKDEscC0xKSksbGFtYmRhLm1pbi5yYXRpbz0wLjAwMikNCnBsb3QobGFzc29fdW5wZW4seHZhciA9ICJsYW1iZGEiLGxhYmVsPVQpDQpgYGANCg0KVGhlIGVtcHR5IG1vZGVsIGNvbnNpc3RzIG5vdyBvZiB0aGUgdW5wZW5hbGl6ZWQgdmFyaWFibGUgYW5kIGl0IGxldmVyYWdlcyB0aGUgZnVsbCBvbWl0dGVkIHZhcmlhYmxlIGJpYXMgZm9yIHByZWRpY3Rpb24uIFRoZSBPVkIgaXMgcmVkdWNlZCBhcyB0aGUgb3RoZXIgdmFyaWFibGVzIHJlY2VpdmUgYXQgbGVhc3QgcGFydHMgb2YgdGhlaXIgY29lZmZpY2llbnRzLiBJbiBwcmluY2lwbGUsIHRoZXJlIHdvdWxkIGJlIGEgc3dlZXQgc3BvdCBvbiB0aGUgdHJhamVjdG9yeSBvZiAkXGJldGFfMSQsIGJ1dCBjcm9zcy12YWxpZGF0aW9uIHNlZW1zIHRvIHN5c3RlbWF0aWNhbGx5IGZhdm9yIG1vZGVscyB3aGVyZSB0aGUgT1ZCIGluICRcYmV0YV8xJCBpcyB1c2VkIGZvciBwcmVkaWN0aW9uLiANCg0KU3VjaCB0aGluZ3MgaGFwcGVuIGlmIHRoZSBNTCBtZXRob2QgaXMgbm90ICJ0YXVnaHQiIHRoYXQgd2UgYXJlIGludGVyZXN0IGluIGEgY2F1c2FsIGVmZmVjdCBpbiB0aGUgY29ycmVjdCB3YXkuDQoNCg0KPGJyPg0KPGJyPg0KDQojIyBUYWtlLWF3YXkNCiANCiAtIElmIHNvbWVib2R5IHRlbGxzIHVzIHRoZSBjb3JyZWN0IG1vZGVsIHdpdGhvdXQgbG9va2luZyBhdCB0aGUgZGF0YSwgbGlmZSBpcyBnb29kLg0KIA0KIC0gSW4gdGhlIGxpa2VseSBjYXNlIHRoYXQgbm90LCB3ZSBuZWVkIHRvIGJlIGNhcmVmdWwgaG93IHdlIGxldmVyYWdlIHRoZSBNTCBtYWNoaW5lcnkgZm9yIGNhdXNhbCBhbmFseXNpcy4NCiANCiAtIFRoZXJlIGFyZSBhIGxvdCBvZiBpbnRlcmVzdGluZyB0aGluZ3MgdG8gYmUgbGVhcm5lZCBhYm91dCBob3cgdG8gdGVhY2ggTUwgdGhhdCB3ZSBhcmUgZXN0aW1hdGluZyBjYXVzYWwgZWZmZWN0cy4NCiANCjxicj4NCjxicj4NCiANCiANCiMjIFN1Z2dlc3Rpb25zIHRvIHBsYXkgd2l0aCB0aGUgdG95IG1vZGVsDQoNCkZlZWwgZnJlZSB0byBwbGF5IGFyb3VuZCB3aXRoIHRoZSBjb2RlLiBUaGlzIGlzIHVzZWZ1bCB0byBzaGFycGVuIGFuZCBjaGFsbGVuZ2UgeW91ciB1bmRlcnN0YW5kaW5nIG9mIHRoZSBtZXRob2RzLiBUaGluayBhYm91dCB0aGUgY29uc2VxdWVuY2VzIG9mIGEgbW9kaWZpY2F0aW9ucyBiZWZvcmUgeW91IHJ1biBpdCBhbmQgY2hlY2sgd2hldGhlciB0aGUgcmVzdWx0cyBhcmUgaW4gbGluZSB3aXRoIHlvdXIgZXhwZWN0YXRpb24uIFNvbWUgc3VnZ2VzdGlvbnM6DQogDQotIE1vZGlmeSBER1AgKGNvcnJlbGF0aW9uIG9mIGNvdmFyaWF0ZXMsIGJldGFzLCBub2lzZSB0ZXJtLCAuLi4pDQoNCi0gSW5jcmVhc2UgdGhlIG51bWJlciBvZiBvYnNlcnZhdGlvbnMNCg0KLSBJbXBsZW1lbnQgb3RoZXIgYWQgaG9jIGlkZWFzIHRoYXQgb25lIGNvdWxkIGNvbWUgdXAgd2l0aA0KDQog