This code performs cross-sectional analysis using both naive and TMLE analyses on the first wave of simulated data created in the data creation code. The code performs a series of analyses:

Firstly, we define SuperLearner libraries to be used by SuperLearner:

SLlib <- c("SL.glm")
SLlib2 <- c("SL.glm","SL.glm.interaction","SL.stepAIC","SL.ranger")

Next, we create a simple cross-sectional dataset from the longitudinal dataset, using just the variables ba, bb, bc, a_0, l_0, and y_0

csdata <- ldata[,c(2,3,4,6,7,8)]

Now, we run TMLE using manual estimation of each of the component models (outcome and propensity), and then updating the outcome model based on those intitial models. It is worth noting that the initial estimate of the outcome model is the same as the ‘naive’ estimate that would be obtained if we were attempting to estimate the effect of exposure without accounting for the exposure-affected confounding.

# Correctly specified
Q0 <- glm(data=csdata,"y_0 ~ a_0 + l_0 + ba + bb + bc")
QAW <- data.frame(cbind(QA=predict(Q0,type="response"),
                        Q0=predict(Q0,type="response",newdata=cbind(csdata[,1:4],a_0=0)),
                        Q1=predict(Q0,type="response",newdata=cbind(csdata[,1:4],a_0=1))))
G <- glm(data=csdata,"a_0 ~ l_0 + ba + bb + bc",family=binomial)
GAW <- predict(G,type="response")
HA1 <- csdata[,5]/GAW
HA0 <- -(1-csdata[,5])/(1-GAW)
H <- HA1+HA0
Q1 <- glm(data=data.frame(cbind(Y=csdata[,6],HA1=HA1,HA0=-HA0,QAW)),"Y ~ -1 + HA1 + HA0 + offset(QA)")
muA1 <- QAW$Q1 + coef(Q1)[1]/GAW
muA0 <- QAW$Q0 + coef(Q1)[2]/(1-GAW)
TMLE <- c(coef=mean(muA1-muA0),
          se=var((HA1-HA0)*(csdata[,6]-QAW$QA) + QAW$Q1 - QAW$Q0 - (muA1-muA0))/length(csdata[,1]))
TMLE
      coef         se 
1.10302092 0.01312571 
# Outcome model mispecified
Q0m1 <- glm(data=csdata,"y_0 ~ a_0 + ba + bb + bc")
QAWm1 <- data.frame(cbind(QA=predict(Q0m1,type="response"),
                          Q0=predict(Q0m1,type="response",newdata=cbind(csdata[,1:4],a_0=0)),
                          Q1=predict(Q0m1,type="response",newdata=cbind(csdata[,1:4],a_0=1))))
Gm1 <- glm(data=csdata,"a_0 ~ l_0 + ba + bb + bc",family=binomial)
GAWm1 <- predict(Gm1,type="response")
HA1m1 <- csdata[,5]/GAWm1
HA0m1 <- -(1-csdata[,5])/(1-GAWm1)
Hm1 <- HA1m1+HA0m1
Q1m1 <- glm(data=data.frame(cbind(Y=csdata[,6],HA1=HA1m1,HA0=-HA0m1,QAWm1)),"Y ~ -1 + HA1 + HA0 + offset(QA)")
muA1m1 <- QAWm1$Q1 + coef(Q1m1)[1]/GAWm1
muA0m1 <- QAWm1$Q0 + coef(Q1m1)[2]/(1-GAWm1)
TMLEm1 <- c(coef=mean(muA1m1-muA0m1),
            se=var((HA1m1-HA0m1)*(csdata[,6]-QAWm1$QA) + QAWm1$Q1 - QAWm1$Q0 - (muA1m1-muA0m1))/length(csdata[,1]))
TMLEm1
      coef         se 
1.10768728 0.01554685 
# Both models mispecified
Q0m2 <- glm(data=csdata,"y_0 ~ a_0 + ba + bb + bc")
QAWm2 <- data.frame(cbind(QA=predict(Q0m2,type="response"),
                          Q0=predict(Q0m2,type="response",newdata=cbind(csdata[,1:4],a_0=0)),
                          Q1=predict(Q0m2,type="response",newdata=cbind(csdata[,1:4],a_0=1))))
Gm2 <- glm(data=csdata,"a_0 ~ ba + bb + bc",family=binomial)
GAWm2 <- predict(Gm2,type="response")
HA1m2 <- csdata[,5]/GAWm2
HA0m2 <- -(1-csdata[,5])/(1-GAWm2)
Hm2 <- HA1m2+HA0m2
Q1m2 <- glm(data=data.frame(cbind(Y=csdata[,6],HA1=HA1m2,HA0=-HA0m2,QAWm2)),"Y ~ -1 + HA1 + HA0 + offset(QA)")
muA1m2 <- QAWm2$Q1 + coef(Q1m2)[1]/GAWm2
muA0m2 <- QAWm2$Q0 + coef(Q1m2)[2]/(1-GAWm2)
TMLEm2 <- c(coef=mean(muA1m2-muA0m2),
            se=var((HA1m2-HA0m2)*(csdata[,6]-QAWm2$QA) + QAWm2$Q1 - QAWm2$Q0 - (muA1m2-muA0m2))/length(csdata[,1]))
TMLEm2
      coef         se 
1.46015483 0.01291413 

Next, we repeat those same analyses, but this time using the ‘tmle’ package

# Correctly specified
rtmle <- tmle(Y=csdata[,6],A=csdata[,5],W=csdata[,1:4],
              Q.SL.library=SLlib,
              g.SL.library=SLlib,
              Qform="Y~A+l_0+ba+bb+bc",
              gform="A~l_0+ba+bb+bc")
summary(rtmle)
 Initial estimation of Q
     Procedure: glm, user-supplied model
     Model:
         Y ~  (Intercept) + A + l_0 + ba + bb + bc

     Coefficients: 
         (Intercept)    0.4305679 
                   A    0.1276895 
                 l_0    0.1125699 
                  ba    0.02584993 
                  bb   -0.01445202 
                  bc    0.03275401 

 Estimation of g (treatment mechanism)
     Procedure: user-supplied regression formula 
     Model:
         A ~  (Intercept) + l_0 + ba + bb + bc 

     Coefficients: 
         (Intercept)   -2.098232 
                 l_0    1.634153 
                  ba    0.2559936 
                  bb   -0.3602987 
                  bc    0.2255404 

 Estimation of g.Z (intermediate variable assignment mechanism)
     Procedure: No intermediate variable 

 Estimation of g.Delta (missingness mechanism)
     Procedure: No missingness 

 Bounds on g: ( 0.025 0.975 )

 Additive Effect
   Parameter Estimate:  1.103
   Estimated Variance:  0.013117
              p-value:  <2e-16
    95% Conf Interval: (0.87855, 1.3275) 

 Additive Effect among the Treated
   Parameter Estimate:  1.1373
   Estimated Variance:  0.011125
              p-value:  <2e-16
    95% Conf Interval: (0.93059, 1.3441) 

 Additive Effect among the Controls
   Parameter Estimate:  1.1009
   Estimated Variance:  0.01473
              p-value:  <2e-16
    95% Conf Interval: (0.86298, 1.3387) 
# Outcome model mispecified
rtmlem1 <- tmle(Y=csdata[,6],A=csdata[,5],W=csdata[,1:4],
                Q.SL.library=SLlib,
                g.SL.library=SLlib,
                Qform="Y~A+ba+bb+bc",
                gform="A~l_0+ba+bb+bc")
summary(rtmlem1)
 Initial estimation of Q
     Procedure: glm, user-supplied model
     Model:
         Y ~  (Intercept) + A + ba + bb + bc

     Coefficients: 
         (Intercept)    0.4474137 
                   A    0.1635434 
                  ba    0.02552904 
                  bb   -0.01169196 
                  bc    0.03284896 

 Estimation of g (treatment mechanism)
     Procedure: user-supplied regression formula 
     Model:
         A ~  (Intercept) + l_0 + ba + bb + bc 

     Coefficients: 
         (Intercept)   -2.098232 
                 l_0    1.634153 
                  ba    0.2559936 
                  bb   -0.3602987 
                  bc    0.2255404 

 Estimation of g.Z (intermediate variable assignment mechanism)
     Procedure: No intermediate variable 

 Estimation of g.Delta (missingness mechanism)
     Procedure: No missingness 

 Bounds on g: ( 0.025 0.975 )

 Additive Effect
   Parameter Estimate:  1.1077
   Estimated Variance:  0.013919
              p-value:  <2e-16
    95% Conf Interval: (0.87647, 1.339) 

 Additive Effect among the Treated
   Parameter Estimate:  1.1433
   Estimated Variance:  0.013972
              p-value:  <2e-16
    95% Conf Interval: (0.91158, 1.3749) 

 Additive Effect among the Controls
   Parameter Estimate:  1.1084
   Estimated Variance:  0.01538
              p-value:  <2e-16
    95% Conf Interval: (0.86531, 1.3515) 
# Both models mispecified
rtmlem2 <- tmle(Y=csdata[,6],A=csdata[,5],W=csdata[,1:4],
                Q.SL.library=SLlib,
                g.SL.library=SLlib,
                Qform="Y~A+ba+bb+bc",
                gform="A~ba+bb+bc")
summary(rtmlem2)
 Initial estimation of Q
     Procedure: glm, user-supplied model
     Model:
         Y ~  (Intercept) + A + ba + bb + bc

     Coefficients: 
         (Intercept)    0.4474137 
                   A    0.1635434 
                  ba    0.02552904 
                  bb   -0.01169196 
                  bc    0.03284896 

 Estimation of g (treatment mechanism)
     Procedure: user-supplied regression formula 
     Model:
         A ~  (Intercept) + ba + bb + bc 

     Coefficients: 
         (Intercept)   -1.614376 
                  ba    0.2488812 
                  bb   -0.3046403 
                  bc    0.2158541 

 Estimation of g.Z (intermediate variable assignment mechanism)
     Procedure: No intermediate variable 

 Estimation of g.Delta (missingness mechanism)
     Procedure: No missingness 

 Bounds on g: ( 0.025 0.975 )

 Additive Effect
   Parameter Estimate:  1.4601
   Estimated Variance:  0.012888
              p-value:  <2e-16
    95% Conf Interval: (1.2376, 1.6826) 

 Additive Effect among the Treated
   Parameter Estimate:  1.4533
   Estimated Variance:  0.011659
              p-value:  <2e-16
    95% Conf Interval: (1.2417, 1.665) 

 Additive Effect among the Controls
   Parameter Estimate:  1.4604
   Estimated Variance:  0.013641
              p-value:  <2e-16
    95% Conf Interval: (1.2315, 1.6893) 

Finally, we carry out analysis using SuperLearner, allowing ‘tmle’ to define the internal models:

sltmle <- tmle(Y=csdata[,6],A=csdata[,5],W=csdata[,1:4],
               Q.SL.library=SLlib2,
               g.SL.library=SLlib2)

Lets see a summary of the results produced by each of the methods, so we can compare them:

Note that the coefficients and standard errors produced by the two methods are almost identical. Differences are due to the fact that ‘tmle’ transforms continuous outcomes into a quasi-binomial variable (continuous, but in the range of 0,1) prior to conducting analysis, and also truncates the propensity score to reduce variability. These differences can be more pronounced, but in this case lead to only small differences between the analyses.

csresults <- matrix(c(coef(summary(Q0))[2,1],coef(summary(Q0))[2,2],
                      coef(summary(Q0m1))[2,1],coef(summary(Q0m1))[2,2],
                      mean(muA1-muA0),sqrt(var((HA1-HA0)*(csdata[,6]-QAW$QA) + QAW$Q1 - QAW$Q0 - (muA1-muA0))/length(csdata[,1])),
                      mean(muA1m1-muA0m1),sqrt(var((HA1m1-HA0m1)*(csdata[,6]-QAWm1$QA) + QAWm1$Q1 - QAWm1$Q0 - (muA1m1-muA0m1))/length(csdata[,1])),
                      mean(muA1m2-muA0m2),sqrt(var((HA1m2-HA0m2)*(csdata[,6]-QAWm2$QA) + QAWm2$Q1 - QAWm2$Q0 - (muA1m2-muA0m2))/length(csdata[,1])),
                      rtmle$estimates$ATE$psi,sqrt(rtmle$estimates$ATE$var.psi),
                      rtmlem1$estimates$ATE$psi,sqrt(rtmlem1$estimates$ATE$var.psi),
                      rtmlem2$estimates$ATE$psi,sqrt(rtmlem2$estimates$ATE$var.psi),
                      sltmle$estimates$ATE$psi,sqrt(sltmle$estimates$ATE$var.psi)),nrow=9,ncol=2,byrow=TRUE)
rownames(csresults) <- c("GLM - correctly specified","GLM - incorrectly specified","Manual TMLE - correctly specified","Manual TMLE - outcome specified","Manual TMLE - doubly specified","'tmle' package - correctly specified","'tmle' package - outcome specified","'tmle' package - doubly specified","SuperLearner TMLE")
colnames(csresults) <- c("Coef","SE")
csresults
                                         Coef         SE
GLM - correctly specified            1.130330 0.09814239
GLM - incorrectly specified          1.447715 0.09919178
Manual TMLE - correctly specified    1.103021 0.11456747
Manual TMLE - outcome specified      1.107687 0.12468699
Manual TMLE - doubly specified       1.460155 0.11364035
'tmle' package - correctly specified 1.103027 0.11452810
'tmle' package - outcome specified   1.107713 0.11798018
'tmle' package - doubly specified    1.460102 0.11352737
SuperLearner TMLE                    1.081280 0.07905581
LS0tDQp0aXRsZTogIkNyb3NzLXNlY3Rpb25hbCBBbmFseXNpcyINCmRhdGU6ICIzIERlY2VtYmVyIDIwMTgiDQpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sNCi0tLQ0KICANCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFfQ0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KGVjaG8gPSBUUlVFKQ0KY2xvdWRzdG9yIDwtICJDOi9Vc2Vycy96MzMxMjkxMS9DbG91ZHN0b3IvIg0KLmxpYlBhdGhzKHBhc3RlMChjbG91ZHN0b3IsIlIgTGlicmFyeSIpKQ0KDQpsaWJyYXJ5KCJ0bWxlIikNCmxpYnJhcnkoImx0bWxlIikNCmxpYnJhcnkoIlN1cGVyTGVhcm5lciIpDQpsaWJyYXJ5KCJzaW1jYXVzYWwiKQ0KbGlicmFyeSgiTUFTUyIpDQpsaWJyYXJ5KCJyYW5nZXIiKQ0KbGlicmFyeSgicGFyYWxsZWwiKQ0KbGlicmFyeSgiZG9QYXJhbGxlbCIpDQpsaWJyYXJ5KCJmb3JlYWNoIikNCmxpYnJhcnkoImxtZTQiKQ0KDQpSTkdraW5kKGtpbmQ9ImRlZmF1bHQiLG5vcm1hbC5raW5kPSJkZWZhdWx0IikNCnNldC5zZWVkKDQzMjM2KQ0KYGBgDQoNClRoaXMgY29kZSBwZXJmb3JtcyBjcm9zcy1zZWN0aW9uYWwgYW5hbHlzaXMgdXNpbmcgYm90aCBuYWl2ZSBhbmQgVE1MRSBhbmFseXNlcyBvbiB0aGUgZmlyc3Qgd2F2ZSBvZiBzaW11bGF0ZWQgZGF0YSBjcmVhdGVkIGluIHRoZSBkYXRhIGNyZWF0aW9uIGNvZGUuIA0KVGhlIGNvZGUgcGVyZm9ybXMgYSBzZXJpZXMgb2YgYW5hbHlzZXM6DQogIA0KKiBOYWl2ZSBhbmFseXNpcyB1c2luZyBHTE1zDQoqIENvcnJlY3RseSBzcGVjaWZpZWQgbWFudWFsIFRNTEUNCiogU2luZ2x5IG1pc3NwZWNpZmllZCAob3V0Y29tZSBtb2RlbCBvbmx5KSBtYW51YWwgVE1MRQ0KKiBEb3VibHkgbWlzc3BlY2lmaWVkIChib3RoIG1vZGVscykgbWFudWFsIFRNTEUNCiogQ29ycmVjdGx5IHNwZWNpZmllZCBUTUxFIHVzaW5nIEdMTXMgb25seSBpbiB0aGUgUiBwYWNrYWdlICd0bWxlJw0KKiBTaW5nbHkgbWlzc3BlY2lmaWVkIChvdXRjb21lIG1vZGVsIG9ubHkpIFRNTEUgdXNpbmcgR0xNcyBvbmx5IGluIHRoZSBSIHBhY2thZ2UgJ3RtbGUnDQoqIERvdWJseSBtaXNzcGVjaWZpZWQgKGJvdGggbW9kZWxzKSBUTUxFIHVzaW5nIEdMTXMgb25seSBpbiB0aGUgUiBwYWNrYWdlICd0bWxlJw0KKiAnQXV0b21hdGljJyBUTUxFIHVzaW5nIFN1cGVyTGVhcm5lciBpbiB0aGUgUiBwYWNrYWdlICd0bWxlJw0KDQpGaXJzdGx5LCB3ZSBkZWZpbmUgU3VwZXJMZWFybmVyIGxpYnJhcmllcyB0byBiZSB1c2VkIGJ5IFN1cGVyTGVhcm5lcjoNCg0KYGBge3Igc2xsaWJ9DQpTTGxpYiA8LSBjKCJTTC5nbG0iKQ0KU0xsaWIyIDwtIGMoIlNMLmdsbSIsIlNMLmdsbS5pbnRlcmFjdGlvbiIsIlNMLnN0ZXBBSUMiLCJTTC5yYW5nZXIiKQ0KYGBgDQoNCk5leHQsIHdlIGNyZWF0ZSBhIHNpbXBsZSBjcm9zcy1zZWN0aW9uYWwgZGF0YXNldCBmcm9tIHRoZSBsb25naXR1ZGluYWwgZGF0YXNldCwgdXNpbmcganVzdCB0aGUgdmFyaWFibGVzIGJhLCBiYiwgYmMsIGFfMCwgbF8wLCBhbmQgeV8wDQoNCmBgYHtyIGNzZGF0YX0NCmNzZGF0YSA8LSBsZGF0YVssYygyLDMsNCw2LDcsOCldDQpgYGANCg0KTm93LCB3ZSBydW4gVE1MRSB1c2luZyBtYW51YWwgZXN0aW1hdGlvbiBvZiBlYWNoIG9mIHRoZSBjb21wb25lbnQgbW9kZWxzIChvdXRjb21lIGFuZCBwcm9wZW5zaXR5KSwgYW5kIHRoZW4gdXBkYXRpbmcgdGhlIG91dGNvbWUgbW9kZWwgYmFzZWQgb24gdGhvc2UgaW50aXRpYWwgbW9kZWxzLg0KSXQgaXMgd29ydGggbm90aW5nIHRoYXQgdGhlIGluaXRpYWwgZXN0aW1hdGUgb2YgdGhlIG91dGNvbWUgbW9kZWwgaXMgdGhlIHNhbWUgYXMgdGhlICduYWl2ZScgZXN0aW1hdGUgdGhhdCB3b3VsZCBiZSBvYnRhaW5lZCBpZiB3ZSB3ZXJlIGF0dGVtcHRpbmcgdG8gZXN0aW1hdGUgdGhlIGVmZmVjdCBvZiBleHBvc3VyZSB3aXRob3V0IGFjY291bnRpbmcgZm9yIHRoZSBleHBvc3VyZS1hZmZlY3RlZCBjb25mb3VuZGluZy4NCg0KYGBge3IgbWFudWFsfQ0KIyBDb3JyZWN0bHkgc3BlY2lmaWVkDQpRMCA8LSBnbG0oZGF0YT1jc2RhdGEsInlfMCB+IGFfMCArIGxfMCArIGJhICsgYmIgKyBiYyIpDQpRQVcgPC0gZGF0YS5mcmFtZShjYmluZChRQT1wcmVkaWN0KFEwLHR5cGU9InJlc3BvbnNlIiksDQogICAgICAgICAgICAgICAgICAgICAgICBRMD1wcmVkaWN0KFEwLHR5cGU9InJlc3BvbnNlIixuZXdkYXRhPWNiaW5kKGNzZGF0YVssMTo0XSxhXzA9MCkpLA0KICAgICAgICAgICAgICAgICAgICAgICAgUTE9cHJlZGljdChRMCx0eXBlPSJyZXNwb25zZSIsbmV3ZGF0YT1jYmluZChjc2RhdGFbLDE6NF0sYV8wPTEpKSkpDQpHIDwtIGdsbShkYXRhPWNzZGF0YSwiYV8wIH4gbF8wICsgYmEgKyBiYiArIGJjIixmYW1pbHk9Ymlub21pYWwpDQpHQVcgPC0gcHJlZGljdChHLHR5cGU9InJlc3BvbnNlIikNCkhBMSA8LSBjc2RhdGFbLDVdL0dBVw0KSEEwIDwtIC0oMS1jc2RhdGFbLDVdKS8oMS1HQVcpDQpIIDwtIEhBMStIQTANClExIDwtIGdsbShkYXRhPWRhdGEuZnJhbWUoY2JpbmQoWT1jc2RhdGFbLDZdLEhBMT1IQTEsSEEwPS1IQTAsUUFXKSksIlkgfiAtMSArIEhBMSArIEhBMCArIG9mZnNldChRQSkiKQ0KbXVBMSA8LSBRQVckUTEgKyBjb2VmKFExKVsxXS9HQVcNCm11QTAgPC0gUUFXJFEwICsgY29lZihRMSlbMl0vKDEtR0FXKQ0KVE1MRSA8LSBjKGNvZWY9bWVhbihtdUExLW11QTApLA0KICAgICAgICAgIHNlPXZhcigoSEExLUhBMCkqKGNzZGF0YVssNl0tUUFXJFFBKSArIFFBVyRRMSAtIFFBVyRRMCAtIChtdUExLW11QTApKS9sZW5ndGgoY3NkYXRhWywxXSkpDQpUTUxFDQoNCiMgT3V0Y29tZSBtb2RlbCBtaXNwZWNpZmllZA0KUTBtMSA8LSBnbG0oZGF0YT1jc2RhdGEsInlfMCB+IGFfMCArIGJhICsgYmIgKyBiYyIpDQpRQVdtMSA8LSBkYXRhLmZyYW1lKGNiaW5kKFFBPXByZWRpY3QoUTBtMSx0eXBlPSJyZXNwb25zZSIpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICBRMD1wcmVkaWN0KFEwbTEsdHlwZT0icmVzcG9uc2UiLG5ld2RhdGE9Y2JpbmQoY3NkYXRhWywxOjRdLGFfMD0wKSksDQogICAgICAgICAgICAgICAgICAgICAgICAgIFExPXByZWRpY3QoUTBtMSx0eXBlPSJyZXNwb25zZSIsbmV3ZGF0YT1jYmluZChjc2RhdGFbLDE6NF0sYV8wPTEpKSkpDQpHbTEgPC0gZ2xtKGRhdGE9Y3NkYXRhLCJhXzAgfiBsXzAgKyBiYSArIGJiICsgYmMiLGZhbWlseT1iaW5vbWlhbCkNCkdBV20xIDwtIHByZWRpY3QoR20xLHR5cGU9InJlc3BvbnNlIikNCkhBMW0xIDwtIGNzZGF0YVssNV0vR0FXbTENCkhBMG0xIDwtIC0oMS1jc2RhdGFbLDVdKS8oMS1HQVdtMSkNCkhtMSA8LSBIQTFtMStIQTBtMQ0KUTFtMSA8LSBnbG0oZGF0YT1kYXRhLmZyYW1lKGNiaW5kKFk9Y3NkYXRhWyw2XSxIQTE9SEExbTEsSEEwPS1IQTBtMSxRQVdtMSkpLCJZIH4gLTEgKyBIQTEgKyBIQTAgKyBvZmZzZXQoUUEpIikNCm11QTFtMSA8LSBRQVdtMSRRMSArIGNvZWYoUTFtMSlbMV0vR0FXbTENCm11QTBtMSA8LSBRQVdtMSRRMCArIGNvZWYoUTFtMSlbMl0vKDEtR0FXbTEpDQpUTUxFbTEgPC0gYyhjb2VmPW1lYW4obXVBMW0xLW11QTBtMSksDQogICAgICAgICAgICBzZT12YXIoKEhBMW0xLUhBMG0xKSooY3NkYXRhWyw2XS1RQVdtMSRRQSkgKyBRQVdtMSRRMSAtIFFBV20xJFEwIC0gKG11QTFtMS1tdUEwbTEpKS9sZW5ndGgoY3NkYXRhWywxXSkpDQpUTUxFbTENCg0KIyBCb3RoIG1vZGVscyBtaXNwZWNpZmllZA0KUTBtMiA8LSBnbG0oZGF0YT1jc2RhdGEsInlfMCB+IGFfMCArIGJhICsgYmIgKyBiYyIpDQpRQVdtMiA8LSBkYXRhLmZyYW1lKGNiaW5kKFFBPXByZWRpY3QoUTBtMix0eXBlPSJyZXNwb25zZSIpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICBRMD1wcmVkaWN0KFEwbTIsdHlwZT0icmVzcG9uc2UiLG5ld2RhdGE9Y2JpbmQoY3NkYXRhWywxOjRdLGFfMD0wKSksDQogICAgICAgICAgICAgICAgICAgICAgICAgIFExPXByZWRpY3QoUTBtMix0eXBlPSJyZXNwb25zZSIsbmV3ZGF0YT1jYmluZChjc2RhdGFbLDE6NF0sYV8wPTEpKSkpDQpHbTIgPC0gZ2xtKGRhdGE9Y3NkYXRhLCJhXzAgfiBiYSArIGJiICsgYmMiLGZhbWlseT1iaW5vbWlhbCkNCkdBV20yIDwtIHByZWRpY3QoR20yLHR5cGU9InJlc3BvbnNlIikNCkhBMW0yIDwtIGNzZGF0YVssNV0vR0FXbTINCkhBMG0yIDwtIC0oMS1jc2RhdGFbLDVdKS8oMS1HQVdtMikNCkhtMiA8LSBIQTFtMitIQTBtMg0KUTFtMiA8LSBnbG0oZGF0YT1kYXRhLmZyYW1lKGNiaW5kKFk9Y3NkYXRhWyw2XSxIQTE9SEExbTIsSEEwPS1IQTBtMixRQVdtMikpLCJZIH4gLTEgKyBIQTEgKyBIQTAgKyBvZmZzZXQoUUEpIikNCm11QTFtMiA8LSBRQVdtMiRRMSArIGNvZWYoUTFtMilbMV0vR0FXbTINCm11QTBtMiA8LSBRQVdtMiRRMCArIGNvZWYoUTFtMilbMl0vKDEtR0FXbTIpDQpUTUxFbTIgPC0gYyhjb2VmPW1lYW4obXVBMW0yLW11QTBtMiksDQogICAgICAgICAgICBzZT12YXIoKEhBMW0yLUhBMG0yKSooY3NkYXRhWyw2XS1RQVdtMiRRQSkgKyBRQVdtMiRRMSAtIFFBV20yJFEwIC0gKG11QTFtMi1tdUEwbTIpKS9sZW5ndGgoY3NkYXRhWywxXSkpDQpUTUxFbTINCmBgYA0KDQpOZXh0LCB3ZSByZXBlYXQgdGhvc2Ugc2FtZSBhbmFseXNlcywgYnV0IHRoaXMgdGltZSB1c2luZyB0aGUgJ3RtbGUnIHBhY2thZ2UNCg0KYGBge3IgYXV0b2dsbX0NCiMgQ29ycmVjdGx5IHNwZWNpZmllZA0KcnRtbGUgPC0gdG1sZShZPWNzZGF0YVssNl0sQT1jc2RhdGFbLDVdLFc9Y3NkYXRhWywxOjRdLA0KICAgICAgICAgICAgICBRLlNMLmxpYnJhcnk9U0xsaWIsDQogICAgICAgICAgICAgIGcuU0wubGlicmFyeT1TTGxpYiwNCiAgICAgICAgICAgICAgUWZvcm09Ill+QStsXzArYmErYmIrYmMiLA0KICAgICAgICAgICAgICBnZm9ybT0iQX5sXzArYmErYmIrYmMiKQ0Kc3VtbWFyeShydG1sZSkNCiMgT3V0Y29tZSBtb2RlbCBtaXNwZWNpZmllZA0KcnRtbGVtMSA8LSB0bWxlKFk9Y3NkYXRhWyw2XSxBPWNzZGF0YVssNV0sVz1jc2RhdGFbLDE6NF0sDQogICAgICAgICAgICAgICAgUS5TTC5saWJyYXJ5PVNMbGliLA0KICAgICAgICAgICAgICAgIGcuU0wubGlicmFyeT1TTGxpYiwNCiAgICAgICAgICAgICAgICBRZm9ybT0iWX5BK2JhK2JiK2JjIiwNCiAgICAgICAgICAgICAgICBnZm9ybT0iQX5sXzArYmErYmIrYmMiKQ0Kc3VtbWFyeShydG1sZW0xKQ0KIyBCb3RoIG1vZGVscyBtaXNwZWNpZmllZA0KcnRtbGVtMiA8LSB0bWxlKFk9Y3NkYXRhWyw2XSxBPWNzZGF0YVssNV0sVz1jc2RhdGFbLDE6NF0sDQogICAgICAgICAgICAgICAgUS5TTC5saWJyYXJ5PVNMbGliLA0KICAgICAgICAgICAgICAgIGcuU0wubGlicmFyeT1TTGxpYiwNCiAgICAgICAgICAgICAgICBRZm9ybT0iWX5BK2JhK2JiK2JjIiwNCiAgICAgICAgICAgICAgICBnZm9ybT0iQX5iYStiYitiYyIpDQpzdW1tYXJ5KHJ0bWxlbTIpDQpgYGANCg0KRmluYWxseSwgd2UgY2Fycnkgb3V0IGFuYWx5c2lzIHVzaW5nIFN1cGVyTGVhcm5lciwgYWxsb3dpbmcgJ3RtbGUnIHRvIGRlZmluZSB0aGUgaW50ZXJuYWwgbW9kZWxzOg0KDQpgYGB7ciBhdXRvc2x9DQpzbHRtbGUgPC0gdG1sZShZPWNzZGF0YVssNl0sQT1jc2RhdGFbLDVdLFc9Y3NkYXRhWywxOjRdLA0KICAgICAgICAgICAgICAgUS5TTC5saWJyYXJ5PVNMbGliMiwNCiAgICAgICAgICAgICAgIGcuU0wubGlicmFyeT1TTGxpYjIpDQpzdW1tYXJ5KHNsdG1sZSkNCmBgYA0KDQpMZXRzIHNlZSBhIHN1bW1hcnkgb2YgdGhlIHJlc3VsdHMgcHJvZHVjZWQgYnkgZWFjaCBvZiB0aGUgbWV0aG9kcywgc28gd2UgY2FuIGNvbXBhcmUgdGhlbToNCg0KTm90ZSB0aGF0IHRoZSBjb2VmZmljaWVudHMgYW5kIHN0YW5kYXJkIGVycm9ycyBwcm9kdWNlZCBieSB0aGUgdHdvIG1ldGhvZHMgYXJlIGFsbW9zdCBpZGVudGljYWwuIERpZmZlcmVuY2VzIGFyZSBkdWUgdG8gdGhlIGZhY3QgdGhhdCAndG1sZScgdHJhbnNmb3JtcyBjb250aW51b3VzIG91dGNvbWVzIGludG8gYSBxdWFzaS1iaW5vbWlhbCB2YXJpYWJsZSAoY29udGludW91cywgYnV0IGluIHRoZSByYW5nZSBvZiAwLDEpIHByaW9yIHRvIGNvbmR1Y3RpbmcgYW5hbHlzaXMsIGFuZCBhbHNvIHRydW5jYXRlcyB0aGUgcHJvcGVuc2l0eSBzY29yZSB0byByZWR1Y2UgdmFyaWFiaWxpdHkuIFRoZXNlIGRpZmZlcmVuY2VzIGNhbiBiZSBtb3JlIHByb25vdW5jZWQsIGJ1dCBpbiB0aGlzIGNhc2UgbGVhZCB0byBvbmx5IHNtYWxsIGRpZmZlcmVuY2VzIGJldHdlZW4gdGhlIGFuYWx5c2VzLg0KDQpgYGB7ciBzdW1tYXJ5fQ0KY3NyZXN1bHRzIDwtIG1hdHJpeChjKGNvZWYoc3VtbWFyeShRMCkpWzIsMV0sY29lZihzdW1tYXJ5KFEwKSlbMiwyXSwNCiAgICAgICAgICAgICAgICAgICAgICBjb2VmKHN1bW1hcnkoUTBtMSkpWzIsMV0sY29lZihzdW1tYXJ5KFEwbTEpKVsyLDJdLA0KICAgICAgICAgICAgICAgICAgICAgIG1lYW4obXVBMS1tdUEwKSxzcXJ0KHZhcigoSEExLUhBMCkqKGNzZGF0YVssNl0tUUFXJFFBKSArIFFBVyRRMSAtIFFBVyRRMCAtIChtdUExLW11QTApKS9sZW5ndGgoY3NkYXRhWywxXSkpLA0KICAgICAgICAgICAgICAgICAgICAgIG1lYW4obXVBMW0xLW11QTBtMSksc3FydCh2YXIoKEhBMW0xLUhBMG0xKSooY3NkYXRhWyw2XS1RQVdtMSRRQSkgKyBRQVdtMSRRMSAtIFFBV20xJFEwIC0gKG11QTFtMS1tdUEwbTEpKS9sZW5ndGgoY3NkYXRhWywxXSkpLA0KICAgICAgICAgICAgICAgICAgICAgIG1lYW4obXVBMW0yLW11QTBtMiksc3FydCh2YXIoKEhBMW0yLUhBMG0yKSooY3NkYXRhWyw2XS1RQVdtMiRRQSkgKyBRQVdtMiRRMSAtIFFBV20yJFEwIC0gKG11QTFtMi1tdUEwbTIpKS9sZW5ndGgoY3NkYXRhWywxXSkpLA0KICAgICAgICAgICAgICAgICAgICAgIHJ0bWxlJGVzdGltYXRlcyRBVEUkcHNpLHNxcnQocnRtbGUkZXN0aW1hdGVzJEFURSR2YXIucHNpKSwNCiAgICAgICAgICAgICAgICAgICAgICBydG1sZW0xJGVzdGltYXRlcyRBVEUkcHNpLHNxcnQocnRtbGVtMSRlc3RpbWF0ZXMkQVRFJHZhci5wc2kpLA0KICAgICAgICAgICAgICAgICAgICAgIHJ0bWxlbTIkZXN0aW1hdGVzJEFURSRwc2ksc3FydChydG1sZW0yJGVzdGltYXRlcyRBVEUkdmFyLnBzaSksDQogICAgICAgICAgICAgICAgICAgICAgc2x0bWxlJGVzdGltYXRlcyRBVEUkcHNpLHNxcnQoc2x0bWxlJGVzdGltYXRlcyRBVEUkdmFyLnBzaSkpLG5yb3c9OSxuY29sPTIsYnlyb3c9VFJVRSkNCnJvd25hbWVzKGNzcmVzdWx0cykgPC0gYygiR0xNIC0gY29ycmVjdGx5IHNwZWNpZmllZCIsIkdMTSAtIGluY29ycmVjdGx5IHNwZWNpZmllZCIsIk1hbnVhbCBUTUxFIC0gY29ycmVjdGx5IHNwZWNpZmllZCIsIk1hbnVhbCBUTUxFIC0gb3V0Y29tZSBzcGVjaWZpZWQiLCJNYW51YWwgVE1MRSAtIGRvdWJseSBzcGVjaWZpZWQiLCIndG1sZScgcGFja2FnZSAtIGNvcnJlY3RseSBzcGVjaWZpZWQiLCIndG1sZScgcGFja2FnZSAtIG91dGNvbWUgbWlzc3BlY2lmaWVkIiwiJ3RtbGUnIHBhY2thZ2UgLSBkb3VibHkgbWlzc3BlY2lmaWVkIiwiU3VwZXJMZWFybmVyIFRNTEUiKQ0KY29sbmFtZXMoY3NyZXN1bHRzKSA8LSBjKCJDb2VmIiwiU0UiKQ0KY3NyZXN1bHRzDQpgYGA=