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==