“Effect or Treatment Heterogeneity? Policy Evaluation with Aggregated and Disaggregated Treatments”

Phillip Heiler and Michael C. Knaus

Scenario 1: Smoking and birth weight

This Notebook replicates the results of section 8.1 using a publicly available random subsample of the data (GitHub repository of Matias Cattaneo) with 5000 instead of roughly 500,000 observations. The notebook should help to …

  • show the underlying code for researchers interested in replicating the analysis or in adapting it to new datasets

  • show that the patterns are similar even with 100 times less data, although they are of course much more noisy

The explanations throughout the notebook are kept short. Please see the paper for more details regarding the decomposition parameters and their estimation.

Click on the “Code” button on the top right of this notebook to “Hide All Code” and see only the results or “Download Rmd” to extract the underlying code.

Running the analyses in this notebook took roughly two hours on a SWITCHengine with eight cores and 32 GB RAM.



Data preparation

Download the data from GitHub into your working directory and set the working directory accordingly.

# Create main variables

# Load required libraries
library(foreign)
library(causalDML)
library(tidyverse)

# Set wd and load paper specific functions
setwd("C:/Users/Administrator/switchdrive/Papers/TH or HTE/Cattaneo2010")

# Set seed
set.seed(1234)

# Download dataset from https://github.com/mdcattaneo/replication-C_2010_JOE/blob/master/C_2010_JOE-dataRS5K.dta
# into your working directory
data = read.dta("C_2010_JOE-dataRS5K.dta")

Now prepare outcome, effective treatment, and confounders as well as the two heterogeneity variables ethnicity and age:

# Create main variables
Y = as.vector(data[,1]) 
T = as.vector(data[,2])
X = as.matrix(data[,4:53])

## Define heterogeneity variables
# Create categorical variable of ethnicity
ethnic = rep("Other",length(Y))
ethnic[X[,2] == 1] = "White"
ethnic[X[,3] == 1] = "Black"
ethnic[X[,4] == 1] = "Hispanic"
ethnic_oh = model.matrix(~0 + ethnic) # One hot coded dummy matrix
colnames(ethnic_oh) = c("Black","Hispanic","Other","White")

# Age
age = X[,44]



Decomposition

Double ML for the effective treatment

The implementation is based on the causalDML package that estimates the average potential outcomes and average treatment effects for the multivalued effective treatment. The nuisance parameters are estimated using an ensemble of methods, which first needs to be initialized. Unlike in the paper with 500k observations where two-fold cross-fitting and -validation is applied, we use 5-folds each with the 5k sample here. The rest is identical.

summary(ate$APO)
       APO         SE
0 3401.296   9.139818
1 3223.077  41.514047
2 3171.331  39.599396
3 3241.900  93.914800
4 3145.123  37.008544
5 3004.499 115.022326
summary(ate$ATE)
           ATE       SE       t         p    
1 - 0 -178.219   42.382 -4.2051 2.655e-05 ***
2 - 0 -229.965   40.458 -5.6841 1.390e-08 ***
3 - 0 -159.395   94.280 -1.6907 0.0909643 .  
4 - 0 -256.173   37.995 -6.7423 1.736e-11 ***
5 - 0 -396.797  115.357 -3.4397 0.0005871 ***
2 - 1  -51.746   57.342 -0.9024 0.3668819    
3 - 1   18.823  102.655  0.1834 0.8545183    
4 - 1  -77.954   55.565 -1.4029 0.1606990    
5 - 1 -218.578  122.276 -1.7876 0.0739043 .  
3 - 2   70.570  101.865  0.6928 0.4884830    
4 - 2  -26.208   54.152 -0.4840 0.6284312    
5 - 2 -166.832  121.637 -1.3716 0.1702647    
4 - 3  -96.778  100.883 -0.9593 0.3374499    
5 - 3 -237.401  148.518 -1.5985 0.1100013    
5 - 4 -140.624  120.830 -1.1638 0.2445530    
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
plot(ate$APO,label=c("None","1-5 cigs","6-10 cigs","11-15 cigs","16-20 cigs","> 20 cigs"))



Decomposition

The decomposition reuses the nuisance parameters and doubly robust scores that are stored in the object created by the causalDML function.

Ethnicity

First, we look at the discrete heterogeneity variable ethnicity and replicate the results of Figure 3a):

## Decomposition for each subgroup
# Regression without constant
decomp_ethnic_woc = HK_decomposition(ate$APO,ethnic_oh,intercept=FALSE)
summary(decomp_ethnic_woc)
nATE:
t test of coefficients:

          Estimate Std. Error  t value  Pr(>|t|)    
zBlack    -131.178     55.934  -2.3452 0.0190551 *  
zHispanic -208.996     61.186  -3.4158 0.0006411 ***
zOther     -69.908     74.544  -0.9378 0.3483862    
zWhite    -254.174     23.634 -10.7544 < 2.2e-16 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

rATE:
t test of coefficients:

          Estimate Std. Error t value  Pr(>|t|)    
zBlack    -164.529     65.902 -2.4966  0.012573 *  
zHispanic -240.730     76.136 -3.1618  0.001577 ** 
zOther     -69.137     78.367 -0.8822  0.377702    
zWhite    -243.488     27.155 -8.9667 < 2.2e-16 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Delta:
t test of coefficients:

           Estimate Std. Error t value Pr(>|t|)
zBlack     33.35066   23.93921  1.3931   0.1636
zHispanic  31.73400   25.44819  1.2470   0.2125
zOther     -0.77172   14.97663 -0.0515   0.9589
zWhite    -10.68540    9.91488 -1.0777   0.2812
plot(decomp_ethnic_woc)

and the results of Figure 3b):

# With white mothers as reference group
decomp_ethnic_wc = HK_decomposition(ate$APO,ethnic_oh[,-4],intercept=TRUE)
summary(decomp_ethnic_wc)
nATE:
t test of coefficients:

            Estimate Std. Error  t value Pr(>|t|)    
(Intercept) -254.174     23.634 -10.7544  < 2e-16 ***
zBlack       122.996     60.723   2.0255  0.04287 *  
zHispanic     45.178     65.592   0.6888  0.49100    
zOther       184.265     78.201   2.3563  0.01850 *  
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

rATE:
t test of coefficients:

             Estimate Std. Error t value Pr(>|t|)    
(Intercept) -243.4883    27.1548 -8.9667  < 2e-16 ***
zBlack        78.9596    71.2775  1.1078  0.26801    
zHispanic      2.7586    80.8336  0.0341  0.97278    
zOther       174.3517    82.9381  2.1022  0.03559 *  
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Delta:
t test of coefficients:

            Estimate Std. Error t value Pr(>|t|)  
(Intercept) -10.6854     9.9149 -1.0777  0.28121  
zBlack       44.0361    25.9112  1.6995  0.08929 .
zHispanic    42.4194    27.3115  1.5532  0.12045  
zOther        9.9137    17.9612  0.5520  0.58101  
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
plot(decomp_ethnic_wc)


Age

Now consider the continuous heterogeneity variable age and estimate the heterogeneity in the decomposition parameters using B-splines as implemented in the crs package. We focus on mothers between 15 and 40 years old and trim the 59 younger or older mothers to avoid extreme behaviour at the boundaries.

# Plot resulting lines
pred_nATE = predict(decomp_age_trim, param = "nATE")
data.frame(z = age[sub],cate = pred_nATE[,1],
           cilow = pred_nATE[,1] - 1.96 * pred_nATE[,2],
           ciup = pred_nATE[,1] + 1.96 * pred_nATE[,2]) %>%
  ggplot(mapping = aes(x = z, y = cate)) +
  geom_line(size = .8) + geom_hline(yintercept=0) +
  geom_ribbon(aes(ymin = cilow,max = ciup),alpha=0.3,fill="darkgreen") +
  theme_bw() + ylab("nATE") + xlab("Age")

pred_rATE = predict(decomp_age_trim, param = "rATE")
data.frame(z = age[sub],cate = pred_rATE[,1],
           cilow = pred_rATE[,1] - 1.96 * pred_rATE[,2],
           ciup = pred_rATE[,1] + 1.96 * pred_rATE[,2]) %>%
  ggplot(mapping = aes(x = z, y = cate)) +
  geom_line(size = .8) + geom_hline(yintercept=0) +
  geom_ribbon(aes(ymin = cilow,max = ciup),alpha=0.3,fill="darkgreen") +
  theme_bw() + ylab("rATE") + xlab("Age")


pred_Delta = predict(decomp_age_trim, param = "Delta")
data.frame(z = age[sub],cate = pred_Delta[,1],
           cilow = pred_Delta[,1] - 1.96 * pred_Delta[,2],
           ciup = pred_Delta[,1] + 1.96 * pred_Delta[,2]) %>%
  ggplot(mapping = aes(x = z, y = cate)) +
  geom_line(size = .8) + geom_hline(yintercept=0) +
  geom_ribbon(aes(ymin = cilow,max = ciup),alpha=0.3,fill="darkgreen") +
  theme_bw() + ylab("Delta") + xlab("Age")

LS0tDQp0aXRsZTogIlJlcGxpY2F0aW9uIE5vdGVib29rIg0Kb3V0cHV0Og0KICBodG1sX25vdGVib29rOiBkZWZhdWx0DQotLS0NCg0KIyAiRWZmZWN0IG9yIFRyZWF0bWVudCBIZXRlcm9nZW5laXR5PyBQb2xpY3kgRXZhbHVhdGlvbiB3aXRoIEFnZ3JlZ2F0ZWQgYW5kIERpc2FnZ3JlZ2F0ZWQgVHJlYXRtZW50cyINCiMjIFBoaWxsaXAgSGVpbGVyIGFuZCBNaWNoYWVsIEMuIEtuYXVzDQoNCiMjIFNjZW5hcmlvIDE6IFNtb2tpbmcgYW5kIGJpcnRoIHdlaWdodA0KDQpUaGlzIE5vdGVib29rIHJlcGxpY2F0ZXMgdGhlIHJlc3VsdHMgb2Ygc2VjdGlvbiA4LjEgdXNpbmcgYSBwdWJsaWNseSBhdmFpbGFibGUgcmFuZG9tIHN1YnNhbXBsZSBvZiB0aGUgZGF0YSAoW0dpdEh1YiByZXBvc2l0b3J5IG9mIE1hdGlhcyBDYXR0YW5lb10oaHR0cHM6Ly9naXRodWIuY29tL21kY2F0dGFuZW8vcmVwbGljYXRpb24tQ18yMDEwX0pPRSkpIHdpdGggNTAwMCBpbnN0ZWFkIG9mIHJvdWdobHkgNTAwLDAwMCBvYnNlcnZhdGlvbnMuIFRoZSBub3RlYm9vayBzaG91bGQgaGVscCB0byAuLi4NCg0KLSBzaG93IHRoZSB1bmRlcmx5aW5nIGNvZGUgZm9yIHJlc2VhcmNoZXJzIGludGVyZXN0ZWQgaW4gcmVwbGljYXRpbmcgdGhlIGFuYWx5c2lzIG9yIGluIGFkYXB0aW5nIGl0IHRvIG5ldyBkYXRhc2V0cw0KDQotIHNob3cgdGhhdCB0aGUgcGF0dGVybnMgYXJlIHNpbWlsYXIgZXZlbiB3aXRoIDEwMCB0aW1lcyBsZXNzIGRhdGEsIGFsdGhvdWdoIHRoZXkgYXJlIG9mIGNvdXJzZSBtdWNoIG1vcmUgbm9pc3kNCg0KVGhlIGV4cGxhbmF0aW9ucyB0aHJvdWdob3V0IHRoZSBub3RlYm9vayBhcmUga2VwdCBzaG9ydC4gUGxlYXNlIHNlZSB0aGUgcGFwZXIgZm9yIG1vcmUgZGV0YWlscyByZWdhcmRpbmcgdGhlIGRlY29tcG9zaXRpb24gcGFyYW1ldGVycyBhbmQgdGhlaXIgZXN0aW1hdGlvbi4NCg0KQ2xpY2sgb24gdGhlICJDb2RlIiBidXR0b24gb24gdGhlIHRvcCByaWdodCBvZiB0aGlzIG5vdGVib29rIHRvICJIaWRlIEFsbCBDb2RlIiBhbmQgc2VlIG9ubHkgdGhlIHJlc3VsdHMgb3IgIkRvd25sb2FkIFJtZCIgdG8gZXh0cmFjdCB0aGUgdW5kZXJseWluZyBjb2RlLg0KDQpSdW5uaW5nIHRoZSBhbmFseXNlcyBpbiB0aGlzIG5vdGVib29rIHRvb2sgcm91Z2hseSB0d28gaG91cnMgb24gYSBbU1dJVENIZW5naW5lXShodHRwczovL3d3dy5zd2l0Y2guY2gvZW5naW5lcy8pIHdpdGggZWlnaHQgY29yZXMgYW5kIDMyIEdCIFJBTS4NCg0KDQo8YnI+DQo8YnI+DQoNCiMjIERhdGEgcHJlcGFyYXRpb24NCg0KRG93bmxvYWQgdGhlIGRhdGEgZnJvbSAqW0dpdEh1Yl0oaHR0cHM6Ly9naXRodWIuY29tL21kY2F0dGFuZW8vcmVwbGljYXRpb24tQ18yMDEwX0pPRSkqIGludG8geW91ciB3b3JraW5nIGRpcmVjdG9yeSBhbmQgc2V0IHRoZSB3b3JraW5nIGRpcmVjdG9yeSBhY2NvcmRpbmdseS4NCg0KDQpgYGB7cn0NCiMgQ3JlYXRlIG1haW4gdmFyaWFibGVzDQoNCiMgTG9hZCByZXF1aXJlZCBsaWJyYXJpZXMNCmxpYnJhcnkoZm9yZWlnbikNCmxpYnJhcnkoY2F1c2FsRE1MKQ0KbGlicmFyeSh0aWR5dmVyc2UpDQoNCiMgU2V0IHdkIGFuZCBsb2FkIHBhcGVyIHNwZWNpZmljIGZ1bmN0aW9ucw0Kc2V0d2QoIkM6L1VzZXJzL0FkbWluaXN0cmF0b3Ivc3dpdGNoZHJpdmUvUGFwZXJzL1RIIG9yIEhURS9DYXR0YW5lbzIwMTAiKQ0KDQojIFNldCBzZWVkDQpzZXQuc2VlZCgxMjM0KQ0KDQojIERvd25sb2FkIGRhdGFzZXQgZnJvbSBodHRwczovL2dpdGh1Yi5jb20vbWRjYXR0YW5lby9yZXBsaWNhdGlvbi1DXzIwMTBfSk9FL2Jsb2IvbWFzdGVyL0NfMjAxMF9KT0UtZGF0YVJTNUsuZHRhDQojIGludG8geW91ciB3b3JraW5nIGRpcmVjdG9yeQ0KZGF0YSA9IHJlYWQuZHRhKCJDXzIwMTBfSk9FLWRhdGFSUzVLLmR0YSIpDQpgYGANCg0KTm93IHByZXBhcmUgb3V0Y29tZSwgZWZmZWN0aXZlIHRyZWF0bWVudCwgYW5kIGNvbmZvdW5kZXJzIGFzIHdlbGwgYXMgdGhlIHR3byBoZXRlcm9nZW5laXR5IHZhcmlhYmxlcyBldGhuaWNpdHkgYW5kIGFnZToNCg0KYGBge3J9DQojIENyZWF0ZSBtYWluIHZhcmlhYmxlcw0KWSA9IGFzLnZlY3RvcihkYXRhWywxXSkgDQpUID0gYXMudmVjdG9yKGRhdGFbLDJdKQ0KWCA9IGFzLm1hdHJpeChkYXRhWyw0OjUzXSkNCg0KIyMgRGVmaW5lIGhldGVyb2dlbmVpdHkgdmFyaWFibGVzDQojIENyZWF0ZSBjYXRlZ29yaWNhbCB2YXJpYWJsZSBvZiBldGhuaWNpdHkNCmV0aG5pYyA9IHJlcCgiT3RoZXIiLGxlbmd0aChZKSkNCmV0aG5pY1tYWywyXSA9PSAxXSA9ICJXaGl0ZSINCmV0aG5pY1tYWywzXSA9PSAxXSA9ICJCbGFjayINCmV0aG5pY1tYWyw0XSA9PSAxXSA9ICJIaXNwYW5pYyINCmV0aG5pY19vaCA9IG1vZGVsLm1hdHJpeCh+MCArIGV0aG5pYykgIyBPbmUgaG90IGNvZGVkIGR1bW15IG1hdHJpeA0KY29sbmFtZXMoZXRobmljX29oKSA9IGMoIkJsYWNrIiwiSGlzcGFuaWMiLCJPdGhlciIsIldoaXRlIikNCg0KIyBBZ2UNCmFnZSA9IFhbLDQ0XQ0KYGBgDQoNCjxicj4NCjxicj4NCg0KIyBEZWNvbXBvc2l0aW9uDQoNCiMjIERvdWJsZSBNTCBmb3IgdGhlIGVmZmVjdGl2ZSB0cmVhdG1lbnQNCg0KVGhlIGltcGxlbWVudGF0aW9uIGlzIGJhc2VkIG9uIHRoZSAqW2NhdXNhbERNTF0oaHR0cHM6Ly9naXRodWIuY29tL01DS25hdXMvY2F1c2FsRE1MKSogcGFja2FnZSB0aGF0IGVzdGltYXRlcyB0aGUgYXZlcmFnZSBwb3RlbnRpYWwgb3V0Y29tZXMgYW5kIGF2ZXJhZ2UgdHJlYXRtZW50IGVmZmVjdHMgZm9yIHRoZSBtdWx0aXZhbHVlZCBlZmZlY3RpdmUgdHJlYXRtZW50LiBUaGUgbnVpc2FuY2UgcGFyYW1ldGVycyBhcmUgZXN0aW1hdGVkIHVzaW5nIGFuIGVuc2VtYmxlIG9mIG1ldGhvZHMsIHdoaWNoIGZpcnN0IG5lZWRzIHRvIGJlIGluaXRpYWxpemVkLiBVbmxpa2UgaW4gdGhlIHBhcGVyIHdpdGggNTAwayBvYnNlcnZhdGlvbnMgd2hlcmUgdHdvLWZvbGQgY3Jvc3MtZml0dGluZyBhbmQgLXZhbGlkYXRpb24gaXMgYXBwbGllZCwgd2UgdXNlIDUtZm9sZHMgZWFjaCB3aXRoIHRoZSA1ayBzYW1wbGUgaGVyZS4gVGhlIHJlc3QgaXMgaWRlbnRpY2FsLg0KDQpgYGB7cn0NCiMjIEluaXRpYWxpemUgY29tcG9uZW50cyB0byBiZSB1c2VkIGluIHRoZSBlbnNlbWJsZSBsZWFybmVyDQojIEdlbmVyYWwgY29tcG9uZW50cw0KbWVhbiA9IGNyZWF0ZV9tZXRob2QoIm1lYW4iLG5hbWU9Ik1lYW4iKQ0KZm9yZXN0ID0gY3JlYXRlX21ldGhvZCgiZm9yZXN0X2dyZiIsbmFtZT0iRm9yZXN0IixhcmdzPWxpc3QoaG9uZXN0eSA9IEYsdHVuZS5wYXJhbWV0ZXJzID0gImFsbCIpKQ0KDQojIFBzY29yZSBzcGVjaWZpYyBjb21wb25lbnRzDQpyaWRnZV9iaW4gPSBjcmVhdGVfbWV0aG9kKCJyaWRnZSIsbmFtZT0iUmlkZ2UiLCBhcmdzPWxpc3QoZmFtaWx5ID0gImJpbm9taWFsIikpDQpsYXNzb19iaW4gPSBjcmVhdGVfbWV0aG9kKCJsYXNzbyIsbmFtZT0iTGFzc28iLGFyZ3M9bGlzdChmYW1pbHkgPSAiYmlub21pYWwiKSkNCg0KIyBPdXRjb21lIHNwZWNpZmljIGNvbXBvbmVudHMNCnJpZGdlX2xzID0gY3JlYXRlX21ldGhvZCgicmlkZ2UiLG5hbWU9IlJpZGdlIikNCmxhc3NvX2xzID0gY3JlYXRlX21ldGhvZCgibGFzc28iLG5hbWU9Ikxhc3NvIikNCg0KIyBSdW4gbXVsdGlwbGUgdHJlYXRtZW50IG1vZGVsIHRvIGdldCBzY29yZXMNCmF0ZSA9IGNhdXNhbERNTChZLFQsWCwNCiAgICAgICAgICAgICAgICBtbF93PWxpc3QobWVhbixmb3Jlc3QscmlkZ2VfYmluLGxhc3NvX2JpbiksDQogICAgICAgICAgICAgICAgbWxfeT1saXN0KG1lYW4sZm9yZXN0LHJpZGdlX2xzLGxhc3NvX2xzKSwNCiAgICAgICAgICAgICAgICBjZj01LGN2PTUpDQoNCiMgU2hvdyByZXN1bHRzIGZvciBlYWNoIGVmZmVjdGl2ZSB0cmVhdG1lbnQNCnN1bW1hcnkoYXRlJEFQTykNCnN1bW1hcnkoYXRlJEFURSkNCnBsb3QoYXRlJEFQTyxsYWJlbD1jKCJOb25lIiwiMS01IGNpZ3MiLCI2LTEwIGNpZ3MiLCIxMS0xNSBjaWdzIiwiMTYtMjAgY2lncyIsIj4gMjAgY2lncyIpKQ0KYGBgDQoNCg0KPGJyPg0KPGJyPg0KDQojIyBEZWNvbXBvc2l0aW9uDQoNClRoZSBkZWNvbXBvc2l0aW9uIHJldXNlcyB0aGUgbnVpc2FuY2UgcGFyYW1ldGVycyBhbmQgZG91Ymx5IHJvYnVzdCBzY29yZXMgdGhhdCBhcmUgc3RvcmVkIGluIHRoZSBvYmplY3QgY3JlYXRlZCBieSB0aGUgKmNhdXNhbERNTCogZnVuY3Rpb24uDQoNCiMjIyBFdGhuaWNpdHkNCg0KRmlyc3QsIHdlIGxvb2sgYXQgdGhlIGRpc2NyZXRlIGhldGVyb2dlbmVpdHkgdmFyaWFibGUgZXRobmljaXR5IGFuZCByZXBsaWNhdGUgdGhlIHJlc3VsdHMgb2YgRmlndXJlIDNhKToNCg0KYGBge3J9DQojIyBEZWNvbXBvc2l0aW9uIGZvciBlYWNoIHN1Ymdyb3VwDQojIFJlZ3Jlc3Npb24gd2l0aG91dCBjb25zdGFudA0KZGVjb21wX2V0aG5pY193b2MgPSBIS19kZWNvbXBvc2l0aW9uKGF0ZSRBUE8sZXRobmljX29oLGludGVyY2VwdD1GQUxTRSkNCnN1bW1hcnkoZGVjb21wX2V0aG5pY193b2MpDQpwbG90KGRlY29tcF9ldGhuaWNfd29jKQ0KYGBgDQoNCmFuZCB0aGUgcmVzdWx0cyBvZiBGaWd1cmUgM2IpOg0KDQpgYGB7cn0NCiMgV2l0aCB3aGl0ZSBtb3RoZXJzIGFzIHJlZmVyZW5jZSBncm91cA0KZGVjb21wX2V0aG5pY193YyA9IEhLX2RlY29tcG9zaXRpb24oYXRlJEFQTyxldGhuaWNfb2hbLC00XSxpbnRlcmNlcHQ9VFJVRSkNCnN1bW1hcnkoZGVjb21wX2V0aG5pY193YykNCnBsb3QoZGVjb21wX2V0aG5pY193YykNCmBgYA0KDQo8YnI+DQoNCg0KIyMjIEFnZQ0KDQpOb3cgY29uc2lkZXIgdGhlIGNvbnRpbnVvdXMgaGV0ZXJvZ2VuZWl0eSB2YXJpYWJsZSBhZ2UgYW5kIGVzdGltYXRlIHRoZSBoZXRlcm9nZW5laXR5IGluIHRoZSBkZWNvbXBvc2l0aW9uIHBhcmFtZXRlcnMgdXNpbmcgQi1zcGxpbmVzIGFzIGltcGxlbWVudGVkIGluIHRoZSAqW2Nyc10oaHR0cHM6Ly9jcmFuLnItcHJvamVjdC5vcmcvd2ViL3BhY2thZ2VzL2Nycy9pbmRleC5odG1sKSogcGFja2FnZS4gV2UgZm9jdXMgb24gbW90aGVycyBiZXR3ZWVuIDE1IGFuZCA0MCB5ZWFycyBvbGQgYW5kIHRyaW0gdGhlIDU5IHlvdW5nZXIgb3Igb2xkZXIgbW90aGVycyB0byBhdm9pZCBleHRyZW1lIGJlaGF2aW91ciBhdCB0aGUgYm91bmRhcmllcy4NCg0KYGBge3J9DQojIFNwbGluZQ0KIyBEZWZpbmUgc3Vic2V0IG9mIDE1LTQwIHlvDQpzdWIgPSAoYWdlPDQxICYgYWdlPjE0KQ0KDQojIFJ1biBjcm9zcy12YWxpZGF0ZWQgc3BsaW5lDQpkZWNvbXBfYWdlX3RyaW0gPSBIS19kZWNvbXBvc2l0aW9uKGF0ZSRBUE8sYWdlLHNwbGluZT1UUlVFLCBzdWJzZXQgPSBzdWIpDQoNCiMgUGxvdCByZXN1bHRpbmcgbGluZXMNCnByZWRfbkFURSA9IHByZWRpY3QoZGVjb21wX2FnZV90cmltLCBwYXJhbSA9ICJuQVRFIikNCmRhdGEuZnJhbWUoeiA9IGFnZVtzdWJdLGNhdGUgPSBwcmVkX25BVEVbLDFdLA0KICAgICAgICAgICBjaWxvdyA9IHByZWRfbkFURVssMV0gLSAxLjk2ICogcHJlZF9uQVRFWywyXSwNCiAgICAgICAgICAgY2l1cCA9IHByZWRfbkFURVssMV0gKyAxLjk2ICogcHJlZF9uQVRFWywyXSkgJT4lDQogIGdncGxvdChtYXBwaW5nID0gYWVzKHggPSB6LCB5ID0gY2F0ZSkpICsNCiAgZ2VvbV9saW5lKHNpemUgPSAuOCkgKyBnZW9tX2hsaW5lKHlpbnRlcmNlcHQ9MCkgKw0KICBnZW9tX3JpYmJvbihhZXMoeW1pbiA9IGNpbG93LG1heCA9IGNpdXApLGFscGhhPTAuMyxmaWxsPSJkYXJrZ3JlZW4iKSArDQogIHRoZW1lX2J3KCkgKyB5bGFiKCJuQVRFIikgKyB4bGFiKCJBZ2UiKQ0KDQpwcmVkX3JBVEUgPSBwcmVkaWN0KGRlY29tcF9hZ2VfdHJpbSwgcGFyYW0gPSAickFURSIpDQpkYXRhLmZyYW1lKHogPSBhZ2Vbc3ViXSxjYXRlID0gcHJlZF9yQVRFWywxXSwNCiAgICAgICAgICAgY2lsb3cgPSBwcmVkX3JBVEVbLDFdIC0gMS45NiAqIHByZWRfckFURVssMl0sDQogICAgICAgICAgIGNpdXAgPSBwcmVkX3JBVEVbLDFdICsgMS45NiAqIHByZWRfckFURVssMl0pICU+JQ0KICBnZ3Bsb3QobWFwcGluZyA9IGFlcyh4ID0geiwgeSA9IGNhdGUpKSArDQogIGdlb21fbGluZShzaXplID0gLjgpICsgZ2VvbV9obGluZSh5aW50ZXJjZXB0PTApICsNCiAgZ2VvbV9yaWJib24oYWVzKHltaW4gPSBjaWxvdyxtYXggPSBjaXVwKSxhbHBoYT0wLjMsZmlsbD0iZGFya2dyZWVuIikgKw0KICB0aGVtZV9idygpICsgeWxhYigickFURSIpICsgeGxhYigiQWdlIikNCg0KcHJlZF9EZWx0YSA9IHByZWRpY3QoZGVjb21wX2FnZV90cmltLCBwYXJhbSA9ICJEZWx0YSIpDQpkYXRhLmZyYW1lKHogPSBhZ2Vbc3ViXSxjYXRlID0gcHJlZF9EZWx0YVssMV0sDQogICAgICAgICAgIGNpbG93ID0gcHJlZF9EZWx0YVssMV0gLSAxLjk2ICogcHJlZF9EZWx0YVssMl0sDQogICAgICAgICAgIGNpdXAgPSBwcmVkX0RlbHRhWywxXSArIDEuOTYgKiBwcmVkX0RlbHRhWywyXSkgJT4lDQogIGdncGxvdChtYXBwaW5nID0gYWVzKHggPSB6LCB5ID0gY2F0ZSkpICsNCiAgZ2VvbV9saW5lKHNpemUgPSAuOCkgKyBnZW9tX2hsaW5lKHlpbnRlcmNlcHQ9MCkgKw0KICBnZW9tX3JpYmJvbihhZXMoeW1pbiA9IGNpbG93LG1heCA9IGNpdXApLGFscGhhPTAuMyxmaWxsPSJkYXJrZ3JlZW4iKSArDQogIHRoZW1lX2J3KCkgKyB5bGFiKCJEZWx0YSIpICsgeGxhYigiQWdlIikNCmBgYA0KDQoNCg0KDQo=