Replication comment: The default values of the mlr3
environment are in constant change. This can lead to different results,
while probably keeping the main insights intact. To make sure you
replicate the results of the paper, run the notebooks within the
replication docker.
This notebook runs the Empirical Monte Carlo Study (EMCS)
illustration described in Section 4.4 of the paper.
First, load packages and set the seed:
library(tidyverse)
library(DoubleML)
library(ggmagnify)
library(grf)
library(hdm)
library(mlr3)
library(mlr3learners)
set.seed(1234)
Next, load the data. Here we use the 401(k) data of the
hdm
package. However, you can adapt the following code
chunk to load any suitable data of your choice. Just make sure to call
the treatment D
, covariates X
, and instrument
Z
. The rest of the notebook should run without further
modifications.
data(pension) # Find variable description if you type ?pension in console
# Treatment
D = pension$p401
# Create main effects matrix
X = model.matrix(~ 0 + age + db + educ + fsize + hown + inc + male + marr + pira + twoearn, data = pension)
# Instrument
Z = pension$e401
Then define the simulation(r,a,b)
functions running the
EMCS r
times with simulated outcome \(Y_i^* = a + b D_i\) and returns the point
estimates:
simulation = function(r,a,b) {
n = length(D) # Assuming all vectors are of the same length
dml_plr_rf = dml_plr_xgb = dml_aipw_rf = dml_aipw_xgb =
dml_plriv_rf = dml_plriv_xgb = dml_aipwiv_rf = dml_aipwiv_xgb = rep(NA,r)
cforest = iforest = matrix(NA,n,r)
for (i in 1:r) {
print(i)
indices = sample(1:n, size = n, replace = TRUE)
d = D[indices]
x = X[indices,]
z = Z[indices]
y = a + b * d
### No instrument
## DoubleML
# matrix interface to DoubleMLData
dml_data = double_ml_data_from_matrix(X=x, y=y, d=d)
# RF
lrn_ranger = lrn("regr.ranger")
lrn_ranger_prob = lrn("classif.ranger")
dml_plr_ranger = DoubleMLPLR$new(dml_data, ml_l=lrn_ranger, ml_m=lrn_ranger)
dml_plr_ranger$fit()
dml_plr_rf[i] = dml_plr_ranger$coef
dml_aipw_ranger = DoubleMLIRM$new(dml_data, lrn_ranger, lrn_ranger_prob)
dml_aipw_ranger$fit()
dml_aipw_rf[i] = dml_aipw_ranger$coef
# xgb
lrn_xgb = lrn("regr.xgboost")
lrn_xgb_prob = lrn("classif.xgboost")
dml_plr_xg = DoubleMLPLR$new(dml_data, ml_l=lrn_xgb, ml_m=lrn_xgb)
dml_plr_xg$fit()
dml_plr_xgb[i] = dml_plr_xg$coef
dml_aipw_xg = DoubleMLIRM$new(dml_data, lrn_xgb, lrn_xgb_prob)
dml_aipw_xg$fit()
dml_aipw_xgb[i] = dml_aipw_xg$coef
## grf
cf = causal_forest(x,y,d)
cforest[,i] = predict(cf)$predictions
### With instrument
## DoubleML
dml_data = double_ml_data_from_matrix(X=x, y=y, d=d, z=z)
dml_plriv_ranger = DoubleMLPLIV$new(dml_data, ml_l=lrn_ranger, ml_m=lrn_ranger, ml_r = lrn_ranger)
dml_plriv_ranger$fit()
dml_plriv_rf[i] = dml_plriv_ranger$coef
dml_aipwiv_ranger = DoubleMLIIVM$new(dml_data, lrn_ranger, lrn_ranger_prob, lrn_ranger_prob)
dml_aipwiv_ranger$fit()
dml_aipwiv_rf[i] = dml_aipwiv_ranger$coef
# xgb
dml_plriv_xg = DoubleMLPLIV$new(dml_data, ml_l=lrn_xgb, ml_m=lrn_xgb, ml_r = lrn_xgb)
dml_plriv_xg$fit()
dml_plriv_xgb[i] = dml_plriv_xg$coef
dml_aipwiv_xg = DoubleMLIIVM$new(dml_data, lrn_xgb, lrn_xgb_prob, lrn_xgb_prob)
dml_aipwiv_xg$fit()
dml_aipwiv_xgb[i] = dml_aipwiv_xg$coef
## grf
cf = instrumental_forest(x,y,d,z)
iforest[,i] = predict(cf)$predictions
}
return(list(dml_plr_rf, dml_plr_xgb, dml_aipw_rf, dml_aipw_xgb, cforest,
dml_plriv_rf, dml_plriv_xgb, dml_aipwiv_rf, dml_aipwiv_xgb, iforest))
}
Run 100 replication with a=b=1
:
results = simulation(100,1,1)
Plot the raw results:
# Customized labels for each vector
custom_labels = c("PLR DML RF", "PLR DML XGB",
"AIPW DML RF", "AIPW DML XGB", "CF grf RF",
"PLR-IV DML RF", "PLR-IV DML XGB",
"Wald-AIPW DML RF", "Wald-AIPW DML XGB", "IF grf RF")
# Initialize an empty data frame to store results
data = data.frame(Value = numeric(0), Group = character(0))
# Loop over the list of vectors and append them to the data frame
for (i in 1:length(custom_labels)) {
# Create a temporary data frame for the current vector
temp_df = data.frame(Value = as.vector(results[[i]]), Group = custom_labels[i])
# Append the temporary data frame to the main data frame
data = rbind(data, temp_df)
}
data$Group = factor(data$Group, levels = rev(custom_labels), label = rev(custom_labels))
# Create the boxplot with ggplot2
g = ggplot(data, aes(x = Group, y = Value)) +
geom_hline(yintercept = 1, linetype = "solid", color="black", linewidth=0.5) +
geom_boxplot(fill="grey") +
coord_flip() + # Makes the boxplots horizontal
theme_light() +
labs(x = "Estimator / Implementation", y = "Estimate", fill = "")
g
Plot the results with zooming (Figure 1 in paper):
figure1 = g + geom_magnify(from = c(xmin = 9.5, xmax = 10.5, ymin = 0.996, ymax = 1.003),
to = c(xmin = 9.35, xmax = 10.45, ymin = 1.05, ymax = 1.19),
corners = 0.1, shadow = TRUE) +
geom_magnify(from = c(xmin = 7.45, xmax = 8.5, ymin = 0.996, ymax = 1.003),
to = c(xmin = 7.45, xmax = 8.55, ymin = 1.05, ymax = 1.19),
corners = 0.1, shadow = TRUE) +
geom_magnify(from = c(xmin = 2.5, xmax = 3.5, ymin = 0.996, ymax = 1.003),
to = c(xmin = 2.55, xmax = 3.65, ymin = 1.05, ymax = 1.19),
corners = 0.1, shadow = TRUE) + ylim(0.5,1.5)
figure1
LS0tCnRpdGxlOiAiVHJlYXRtZW50IEVmZmVjdCBFc3RpbWF0b3JzIGFzIFdlaWdodGVkIE91dGNvbWVzIgpzdWJ0aXRsZTogIkVtcGlyaWNhbCBNb250ZSBDYXJsbyBpbGx1c3RyYXRpb24iCmF1dGhvcjogIk1pY2hhZWwgQy4gS25hdXMiCmRhdGU6ICIxMS8yNCIKb3V0cHV0OiAKICBodG1sX25vdGVib29rOgogICAgdG9jOiB0cnVlCiAgICB0b2NfZmxvYXQ6IHRydWUKICAgIGNvZGVfZm9sZGluZzogc2hvdwotLS0KCipSZXBsaWNhdGlvbiBjb21tZW50OiBUaGUgZGVmYXVsdCB2YWx1ZXMgb2YgdGhlIGBtbHIzYCBlbnZpcm9ubWVudCBhcmUgaW4gY29uc3RhbnQgY2hhbmdlLiBUaGlzIGNhbiBsZWFkIHRvIGRpZmZlcmVudCByZXN1bHRzLCB3aGlsZSBwcm9iYWJseSBrZWVwaW5nIHRoZSBtYWluIGluc2lnaHRzIGludGFjdC4gVG8gbWFrZSBzdXJlIHlvdSByZXBsaWNhdGUgdGhlIHJlc3VsdHMgb2YgdGhlIHBhcGVyLCBydW4gdGhlIG5vdGVib29rcyB3aXRoaW4gdGhlIHJlcGxpY2F0aW9uIGRvY2tlci4qCgpUaGlzIG5vdGVib29rIHJ1bnMgdGhlIEVtcGlyaWNhbCBNb250ZSBDYXJsbyBTdHVkeSAoRU1DUykgaWxsdXN0cmF0aW9uIGRlc2NyaWJlZCBpbiBTZWN0aW9uIDQuNCBvZiB0aGUgcGFwZXIuCgpGaXJzdCwgbG9hZCBwYWNrYWdlcyBhbmQgc2V0IHRoZSBzZWVkOgoKYGBge3IsIG1lc3NhZ2UgPSBGQUxTRSwgd2FybmluZz1GQUxTRX0KbGlicmFyeSh0aWR5dmVyc2UpCmxpYnJhcnkoRG91YmxlTUwpCmxpYnJhcnkoZ2dtYWduaWZ5KQpsaWJyYXJ5KGdyZikKbGlicmFyeShoZG0pCmxpYnJhcnkobWxyMykKbGlicmFyeShtbHIzbGVhcm5lcnMpCgpzZXQuc2VlZCgxMjM0KQpgYGAKCk5leHQsIGxvYWQgdGhlIGRhdGEuIEhlcmUgd2UgdXNlIHRoZSA0MDEoaykgZGF0YSBvZiB0aGUgYGhkbWAgcGFja2FnZS4gSG93ZXZlciwgeW91IGNhbiBhZGFwdCB0aGUgZm9sbG93aW5nIGNvZGUgY2h1bmsgdG8gbG9hZCBhbnkgc3VpdGFibGUgZGF0YSBvZiB5b3VyIGNob2ljZS4gSnVzdCBtYWtlIHN1cmUgdG8gY2FsbCB0aGUgdHJlYXRtZW50IGBEYCwgY292YXJpYXRlcyBgWGAsIGFuZCBpbnN0cnVtZW50IGBaYC4gVGhlIHJlc3Qgb2YgdGhlIG5vdGVib29rIHNob3VsZCBydW4gd2l0aG91dCBmdXJ0aGVyIG1vZGlmaWNhdGlvbnMuCgpgYGB7cn0KZGF0YShwZW5zaW9uKSAjIEZpbmQgdmFyaWFibGUgZGVzY3JpcHRpb24gaWYgeW91IHR5cGUgP3BlbnNpb24gaW4gY29uc29sZQoKIyBUcmVhdG1lbnQKRCA9IHBlbnNpb24kcDQwMQojIENyZWF0ZSBtYWluIGVmZmVjdHMgbWF0cml4ClggPSBtb2RlbC5tYXRyaXgofiAwICsgYWdlICsgZGIgKyBlZHVjICsgZnNpemUgKyBob3duICsgaW5jICsgbWFsZSArIG1hcnIgKyBwaXJhICsgdHdvZWFybiwgZGF0YSA9IHBlbnNpb24pCiMgSW5zdHJ1bWVudApaID0gcGVuc2lvbiRlNDAxCmBgYAoKClRoZW4gZGVmaW5lIHRoZSBgc2ltdWxhdGlvbihyLGEsYilgIGZ1bmN0aW9ucyBydW5uaW5nIHRoZSBFTUNTIGByYCB0aW1lcyB3aXRoIHNpbXVsYXRlZCBvdXRjb21lICRZX2leKiA9IGEgKyBiIERfaSQgYW5kIHJldHVybnMgdGhlIHBvaW50IGVzdGltYXRlczoKCmBgYHtyfQpzaW11bGF0aW9uID0gZnVuY3Rpb24ocixhLGIpIHsKICBuID0gbGVuZ3RoKEQpICMgQXNzdW1pbmcgYWxsIHZlY3RvcnMgYXJlIG9mIHRoZSBzYW1lIGxlbmd0aAogIGRtbF9wbHJfcmYgPSBkbWxfcGxyX3hnYiA9IGRtbF9haXB3X3JmID0gZG1sX2FpcHdfeGdiID0gCiAgZG1sX3Bscml2X3JmID0gZG1sX3Bscml2X3hnYiA9IGRtbF9haXB3aXZfcmYgPSBkbWxfYWlwd2l2X3hnYiA9IHJlcChOQSxyKQogIGNmb3Jlc3QgPSBpZm9yZXN0ID0gbWF0cml4KE5BLG4scikKICAKICBmb3IgKGkgaW4gMTpyKSB7CiAgICBwcmludChpKQogICAgCiAgICBpbmRpY2VzID0gc2FtcGxlKDE6biwgc2l6ZSA9IG4sIHJlcGxhY2UgPSBUUlVFKQogICAgZCA9IERbaW5kaWNlc10KICAgIHggPSBYW2luZGljZXMsXQogICAgeiA9IFpbaW5kaWNlc10KICAgIHkgPSBhICsgYiAqIGQKICAgIAogICAgIyMjIE5vIGluc3RydW1lbnQKICAgICMjIERvdWJsZU1MCiAgICAjIG1hdHJpeCBpbnRlcmZhY2UgdG8gRG91YmxlTUxEYXRhCiAgICBkbWxfZGF0YSA9IGRvdWJsZV9tbF9kYXRhX2Zyb21fbWF0cml4KFg9eCwgeT15LCBkPWQpCiAgICAjIFJGCiAgICBscm5fcmFuZ2VyID0gbHJuKCJyZWdyLnJhbmdlciIpCiAgICBscm5fcmFuZ2VyX3Byb2IgPSBscm4oImNsYXNzaWYucmFuZ2VyIikKICAgIAogICAgZG1sX3Bscl9yYW5nZXIgPSBEb3VibGVNTFBMUiRuZXcoZG1sX2RhdGEsIG1sX2w9bHJuX3JhbmdlciwgbWxfbT1scm5fcmFuZ2VyKQogICAgZG1sX3Bscl9yYW5nZXIkZml0KCkKICAgIGRtbF9wbHJfcmZbaV0gPSBkbWxfcGxyX3JhbmdlciRjb2VmCiAgICAKICAgIGRtbF9haXB3X3JhbmdlciA9IERvdWJsZU1MSVJNJG5ldyhkbWxfZGF0YSwgbHJuX3JhbmdlciwgbHJuX3Jhbmdlcl9wcm9iKQogICAgZG1sX2FpcHdfcmFuZ2VyJGZpdCgpCiAgICBkbWxfYWlwd19yZltpXSA9IGRtbF9haXB3X3JhbmdlciRjb2VmCiAgICAKICAgICMgeGdiCiAgICBscm5feGdiID0gbHJuKCJyZWdyLnhnYm9vc3QiKQogICAgbHJuX3hnYl9wcm9iID0gbHJuKCJjbGFzc2lmLnhnYm9vc3QiKQogICAgCiAgICBkbWxfcGxyX3hnID0gRG91YmxlTUxQTFIkbmV3KGRtbF9kYXRhLCBtbF9sPWxybl94Z2IsIG1sX209bHJuX3hnYikKICAgIGRtbF9wbHJfeGckZml0KCkKICAgIGRtbF9wbHJfeGdiW2ldID0gZG1sX3Bscl94ZyRjb2VmCiAgICAKICAgIGRtbF9haXB3X3hnID0gRG91YmxlTUxJUk0kbmV3KGRtbF9kYXRhLCBscm5feGdiLCBscm5feGdiX3Byb2IpCiAgICBkbWxfYWlwd194ZyRmaXQoKQogICAgZG1sX2FpcHdfeGdiW2ldID0gZG1sX2FpcHdfeGckY29lZgogICAgCiAgICAjIyBncmYKICAgIGNmID0gY2F1c2FsX2ZvcmVzdCh4LHksZCkKICAgIGNmb3Jlc3RbLGldID0gcHJlZGljdChjZikkcHJlZGljdGlvbnMKICAgIAogICAgCiAgICAjIyMgV2l0aCBpbnN0cnVtZW50CiAgICAjIyBEb3VibGVNTAogICAgZG1sX2RhdGEgPSBkb3VibGVfbWxfZGF0YV9mcm9tX21hdHJpeChYPXgsIHk9eSwgZD1kLCB6PXopCiAgICBkbWxfcGxyaXZfcmFuZ2VyID0gRG91YmxlTUxQTElWJG5ldyhkbWxfZGF0YSwgbWxfbD1scm5fcmFuZ2VyLCBtbF9tPWxybl9yYW5nZXIsIG1sX3IgPSBscm5fcmFuZ2VyKQogICAgZG1sX3Bscml2X3JhbmdlciRmaXQoKQogICAgZG1sX3Bscml2X3JmW2ldID0gZG1sX3Bscml2X3JhbmdlciRjb2VmCiAgICAKICAgIGRtbF9haXB3aXZfcmFuZ2VyID0gRG91YmxlTUxJSVZNJG5ldyhkbWxfZGF0YSwgbHJuX3JhbmdlciwgbHJuX3Jhbmdlcl9wcm9iLCBscm5fcmFuZ2VyX3Byb2IpCiAgICBkbWxfYWlwd2l2X3JhbmdlciRmaXQoKQogICAgZG1sX2FpcHdpdl9yZltpXSA9IGRtbF9haXB3aXZfcmFuZ2VyJGNvZWYKICAgIAogICAgCiAgICAjIHhnYgogICAgZG1sX3Bscml2X3hnID0gRG91YmxlTUxQTElWJG5ldyhkbWxfZGF0YSwgbWxfbD1scm5feGdiLCBtbF9tPWxybl94Z2IsIG1sX3IgPSBscm5feGdiKQogICAgZG1sX3Bscml2X3hnJGZpdCgpCiAgICBkbWxfcGxyaXZfeGdiW2ldID0gZG1sX3Bscml2X3hnJGNvZWYKICAgIAogICAgZG1sX2FpcHdpdl94ZyA9IERvdWJsZU1MSUlWTSRuZXcoZG1sX2RhdGEsIGxybl94Z2IsIGxybl94Z2JfcHJvYiwgbHJuX3hnYl9wcm9iKQogICAgZG1sX2FpcHdpdl94ZyRmaXQoKQogICAgZG1sX2FpcHdpdl94Z2JbaV0gPSBkbWxfYWlwd2l2X3hnJGNvZWYKICAgIAogICAgIyMgZ3JmCiAgICBjZiA9IGluc3RydW1lbnRhbF9mb3Jlc3QoeCx5LGQseikKICAgIGlmb3Jlc3RbLGldID0gcHJlZGljdChjZikkcHJlZGljdGlvbnMKICAgIAogIH0KICByZXR1cm4obGlzdChkbWxfcGxyX3JmLCBkbWxfcGxyX3hnYiwgZG1sX2FpcHdfcmYsIGRtbF9haXB3X3hnYiwgY2ZvcmVzdCwKICAgICAgICAgICAgICBkbWxfcGxyaXZfcmYsIGRtbF9wbHJpdl94Z2IsIGRtbF9haXB3aXZfcmYsIGRtbF9haXB3aXZfeGdiLCBpZm9yZXN0KSkKfQpgYGAKCgpSdW4gMTAwIHJlcGxpY2F0aW9uIHdpdGggYGE9Yj0xYDoKCmBgYHtyLCByZXN1bHRzPSdoaWRlJ30KcmVzdWx0cyA9IHNpbXVsYXRpb24oMTAwLDEsMSkKYGBgCgoKUGxvdCB0aGUgcmF3IHJlc3VsdHM6CgpgYGB7cn0KIyBDdXN0b21pemVkIGxhYmVscyBmb3IgZWFjaCB2ZWN0b3IKY3VzdG9tX2xhYmVscyA9IGMoIlBMUiBETUwgUkYiLCAiUExSIERNTCBYR0IiLCAKICAgICAgICAgICAgICAgICAgICJBSVBXIERNTCBSRiIsICJBSVBXIERNTCBYR0IiLCAiQ0YgZ3JmIFJGIiwKICAgICAgICAgICAgICAgICAgICJQTFItSVYgRE1MIFJGIiwgIlBMUi1JViBETUwgWEdCIiwgCiAgICAgICAgICAgICAgICAgICAiV2FsZC1BSVBXIERNTCBSRiIsICJXYWxkLUFJUFcgRE1MIFhHQiIsICJJRiBncmYgUkYiKQoKIyBJbml0aWFsaXplIGFuIGVtcHR5IGRhdGEgZnJhbWUgdG8gc3RvcmUgcmVzdWx0cwpkYXRhID0gZGF0YS5mcmFtZShWYWx1ZSA9IG51bWVyaWMoMCksIEdyb3VwID0gY2hhcmFjdGVyKDApKQoKIyBMb29wIG92ZXIgdGhlIGxpc3Qgb2YgdmVjdG9ycyBhbmQgYXBwZW5kIHRoZW0gdG8gdGhlIGRhdGEgZnJhbWUKZm9yIChpIGluIDE6bGVuZ3RoKGN1c3RvbV9sYWJlbHMpKSB7CiAgIyBDcmVhdGUgYSB0ZW1wb3JhcnkgZGF0YSBmcmFtZSBmb3IgdGhlIGN1cnJlbnQgdmVjdG9yCiAgdGVtcF9kZiA9IGRhdGEuZnJhbWUoVmFsdWUgPSBhcy52ZWN0b3IocmVzdWx0c1tbaV1dKSwgR3JvdXAgPSBjdXN0b21fbGFiZWxzW2ldKQogIAogICMgQXBwZW5kIHRoZSB0ZW1wb3JhcnkgZGF0YSBmcmFtZSB0byB0aGUgbWFpbiBkYXRhIGZyYW1lCiAgZGF0YSA9IHJiaW5kKGRhdGEsIHRlbXBfZGYpCn0KCmRhdGEkR3JvdXAgPSBmYWN0b3IoZGF0YSRHcm91cCwgbGV2ZWxzID0gcmV2KGN1c3RvbV9sYWJlbHMpLCBsYWJlbCA9IHJldihjdXN0b21fbGFiZWxzKSkKICAKIyBDcmVhdGUgdGhlIGJveHBsb3Qgd2l0aCBnZ3Bsb3QyCmcgPSBnZ3Bsb3QoZGF0YSwgYWVzKHggPSBHcm91cCwgeSA9IFZhbHVlKSkgKwogIGdlb21faGxpbmUoeWludGVyY2VwdCA9IDEsIGxpbmV0eXBlID0gInNvbGlkIiwgY29sb3I9ImJsYWNrIiwgbGluZXdpZHRoPTAuNSkgKwogIGdlb21fYm94cGxvdChmaWxsPSJncmV5IikgKwogIGNvb3JkX2ZsaXAoKSArICMgTWFrZXMgdGhlIGJveHBsb3RzIGhvcml6b250YWwKICB0aGVtZV9saWdodCgpICsKICBsYWJzKHggPSAiRXN0aW1hdG9yIC8gSW1wbGVtZW50YXRpb24iLCB5ID0gIkVzdGltYXRlIiwgZmlsbCA9ICIiKQpnCmBgYAoKClBsb3QgdGhlIHJlc3VsdHMgd2l0aCB6b29taW5nIChGaWd1cmUgMSBpbiBwYXBlcik6CgpgYGB7cn0KZmlndXJlMSA9IGcgKyBnZW9tX21hZ25pZnkoZnJvbSA9IGMoeG1pbiA9IDkuNSwgeG1heCA9IDEwLjUsIHltaW4gPSAwLjk5NiwgeW1heCA9IDEuMDAzKSwKICAgICAgICAgICAgICAgICAgICAgICAgICB0byA9IGMoeG1pbiA9IDkuMzUsIHhtYXggPSAxMC40NSwgeW1pbiA9IDEuMDUsIHltYXggPSAxLjE5KSwKICAgICAgICAgICAgICAgICBjb3JuZXJzID0gMC4xLCBzaGFkb3cgPSBUUlVFKSArIAogIGdlb21fbWFnbmlmeShmcm9tID0gYyh4bWluID0gNy40NSwgeG1heCA9IDguNSwgeW1pbiA9IDAuOTk2LCB5bWF4ID0gMS4wMDMpLAogICAgICAgICAgICAgICB0byA9IGMoeG1pbiA9IDcuNDUsIHhtYXggPSA4LjU1LCB5bWluID0gMS4wNSwgeW1heCA9IDEuMTkpLAogICAgICAgICAgICAgICBjb3JuZXJzID0gMC4xLCBzaGFkb3cgPSBUUlVFKSArCiAgZ2VvbV9tYWduaWZ5KGZyb20gPSBjKHhtaW4gPSAyLjUsIHhtYXggPSAzLjUsIHltaW4gPSAwLjk5NiwgeW1heCA9IDEuMDAzKSwgCiAgICAgICAgICAgICAgIHRvID0gYyh4bWluID0gMi41NSwgeG1heCA9IDMuNjUsIHltaW4gPSAxLjA1LCB5bWF4ID0gMS4xOSksCiAgICAgICAgICAgICAgIGNvcm5lcnMgPSAwLjEsIHNoYWRvdyA9IFRSVUUpICsgeWxpbSgwLjUsMS41KQpmaWd1cmUxCmBgYAoKCgoKYGBge3IsIGVjaG89Rn0KIyBUaGlzIHBhcnQgaXMgcmVsZXZhbnQgaWYgeW91IHJ1biB0aGUgbm90ZWJvb2tzIGluc2lkZSB0aGUgZG9ja2VyIGFuZCB3YW50IHRvIHNhdmUgZ3JhcGhzIGFuZCBpbWFnZSBpbiBhIHNoYXJlZCBob3N0IHZvbHVtZSBjYWxsZWQgc2hhcmVkX2ZpbGVzICh1bmNvbW1lbnQgYW5kL29yIGFkanVzdCBvbiBkZW1hbmQpOgoKIyBnZ3NhdmUoIi9ob21lL3JzdHVkaW8vc2hhcmVkX2ZpbGVzL0ZpZ3VyZTEucGRmIiwgcGxvdCA9IGZpZ3VyZTEsIHdpZHRoID0gNywgaGVpZ2h0ID0gMy41LCBkcGkgPSAzMDApCiMgZ2dzYXZlKCIvaG9tZS9yc3R1ZGlvL3NoYXJlZF9maWxlcy9GaWd1cmUxLnBuZyIsIHBsb3QgPSBmaWd1cmUxLCB3aWR0aCA9IDcsIGhlaWdodCA9IDMuNSwgZHBpID0gODAwKQojIHNhdmUuaW1hZ2UoZmlsZSA9ICIvaG9tZS9yc3R1ZGlvL3NoYXJlZF9maWxlcy9FTUNTX2lsbHVzdHJhdGlvbl80MDFrLlJEYXRhIikKYGBgCg==