This code performs longitudinal 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")
SLlib3 <- list(Q=c("SL.glm","SL.glm.interaction","SL.stepAIC"),
               g=c("SL.glm","SL.glm.interaction","SL.stepAIC","SL.ranger"))

Next, we define the models to be used by ‘ltmle’ when manually specifying models. ‘ltmle’ requires models to be defined for each exposure and censoring variable in ‘gform’ and the first in each block of confounders and each outcome in ‘qform’. In this case, there is only one outcome, so qform only contains one outcome model. ‘ltmle’ can also produce a series of q and g models automatically based on the data - if qform and gform are not specified in the command, ‘ltmle’ will produce a set of all required models, using all predictor variables that preceed that variable in the data. In many cases this is perfectly acceptable (in this case, the correctly specified models are the same as the automatically produce models); however, the models produced automatically can be incorrect when variables should not be included in some of the component models - for example, when predictors of censoring are not the same as predictors of exposure.

# Correctly specified confounder/outcome models
qforma <- c(l_0="Q.kplus1 ~ ba + bb + bc",
            l_1="Q.kplus1 ~ ba + bb + bc + l_0 + a_0",
            l_2="Q.kplus1 ~ ba + bb + bc + l_0 + a_0 + l_1 + a_1",
            l_3="Q.kplus1 ~ ba + bb + bc + l_0 + a_0 + l_1 + a_1 + l_2 + a_2",
            l_4="Q.kplus1 ~ ba + bb + bc + l_0 + a_0 + l_1 + a_1 + l_2 + a_2 + l_3 + a_3",
            y_4="Q.kplus1 ~ ba + bb + bc + l_0 + a_0 + l_1 + a_1 + l_2 + a_2 + l_3 + a_3 + l_4 + a_4")
# Incorrectly specified confounder/outcome models
mqforma <- c(l_0="Q.kplus1 ~ ba + bb + bc",
             l_1="Q.kplus1 ~ ba + bb + bc + a_0",
             l_2="Q.kplus1 ~ ba + bb + bc + a_0 + a_1",
             l_3="Q.kplus1 ~ ba + bb + bc + a_0 + a_1 + a_2",
             l_4="Q.kplus1 ~ ba + bb + bc + a_0 + a_1 + a_2 + a_3",
             y_4="Q.kplus1 ~ ba + bb + bc + a_0 + a_1 + a_2 + a_3 + a_4")
# Correctly specified exposure/censoring models
gforma <- c(c_0="c_0 ~ ba + bb + bc",
            a_0="a_0 ~ ba + bb + bc + l_0",
            c_1="c_1 ~ ba + bb + bc + l_0 + a_0 ",
            a_1="a_1 ~ ba + bb + bc + l_0 + a_0 + l_1",
            c_2="c_2 ~ ba + bb + bc + l_0 + a_0 + l_1 + a_1",
            a_2="a_2 ~ ba + bb + bc + l_0 + a_0 + l_1 + a_1 + l_2",
            c_3="c_3 ~ ba + bb + bc + l_0 + a_0 + l_1 + a_1 + l_2 + a_2",
            a_3="a_3 ~ ba + bb + bc + l_0 + a_0 + l_1 + a_1 + l_2 + a_2 + l_3",
            c_4="c_4 ~ ba + bb + bc + l_0 + a_0 + l_1 + a_1 + l_2 + a_2 + l_3 + a_3",
            a_4="a_4 ~ ba + bb + bc + l_0 + a_0 + l_1 + a_1 + l_2 + a_2 + l_3 + a_3 + l_4")
# Incorrectly specified exposure/censoring models
mgforma <- c(c_0="c_0 ~ ba + bb + bc",
             a_0="a_0 ~ ba + bb + bc",
             c_1="c_1 ~ ba + bb + bc + a_0",
             a_1="a_1 ~ ba + bb + bc + a_0",
             c_2="c_2 ~ ba + bb + bc + a_0 + a_1",
             a_2="a_2 ~ ba + bb + bc + a_0 + a_1",
             c_3="c_3 ~ ba + bb + bc + a_0 + a_1 + a_2",
             a_3="a_3 ~ ba + bb + bc + a_0 + a_1 + a_2",
             c_4="c_4 ~ ba + bb + bc + a_0 + a_1 + a_2 + a_3",
             a_4="a_4 ~ ba + bb + bc + a_0 + a_1 + a_2 + a_3")

Now, we create a data subset with all observations of exposure and confounders, but only final outcome Y4:

ldata2 <- ldata[,c(-1,-8,-12,-16,-20)]

Now we can begin analysis. Firstly, we run TMLE using the ‘ltmle’ package, but manually specifying the models to be used (as defined above), and with estimation conducted using only generalised linear models. Note that, because ‘ltmle’ checks and transforms continuous outcomes, and checks that data is always missing after censoring. For continuous outcomes, the variable is truncated to a quasibinomial distribution (continous but bounded in 0/1); for missing data, and observations after a censoring event are ignored. Because of these checks, the command potentially produces a number of messages. These are not an issue, and have been left enabled for the first analysis to show what they look like, but have been disabled in subsequent analyses to simplify this markdown document.

# Correctly specified
# Correctly specified
rltmle1 <- suppressWarnings(ltmle(ldata2,
                                  Anodes=c("a_0","a_1","a_2","a_3","a_4"),
                                  Lnodes=c("l_0","l_1","l_2","l_3","l_4"),
                                  Cnodes=c("c_0","c_1","c_2","c_3","c_4"),
                                  Ynodes="y_4",
                                  abar=list(c(1,1,1,1,1),c(0,0,0,0,0)),
                                  SL.library=SLlib,
                                  Qform=qforma,gform=gforma,
                                  estimate.time=FALSE,
                                  survivalOutcome=FALSE))
Note: for internal purposes, all nodes after a censoring event are set to NA and 
 all nodes (except Ynodes) are set to NA after Y=1 if survivalFunction is TRUE (or if specified by deterministic.Q.function).
 Your data did not conform and has been adjusted. This may be relevant if you are 
 writing your own deterministic function(s) or debugging ltmle.
Note: for internal purposes, all nodes after a censoring event are set to NA and 
 all nodes (except Ynodes) are set to NA after Y=1 if survivalFunction is TRUE (or if specified by deterministic.Q.function).
 Your data did not conform and has been adjusted. This may be relevant if you are 
 writing your own deterministic function(s) or debugging ltmle.
Note: for internal purposes, all nodes after a censoring event are set to NA and 
 all nodes (except Ynodes) are set to NA after Y=1 if survivalFunction is TRUE (or if specified by deterministic.Q.function).
 Your data did not conform and has been adjusted. This may be relevant if you are 
 writing your own deterministic function(s) or debugging ltmle.
Note: for internal purposes, all nodes after a censoring event are set to NA and 
 all nodes (except Ynodes) are set to NA after Y=1 if survivalFunction is TRUE (or if specified by deterministic.Q.function).
 Your data did not conform and has been adjusted. This may be relevant if you are 
 writing your own deterministic function(s) or debugging ltmle.
Note: for internal purposes, all nodes after a censoring event are set to NA and 
 all nodes (except Ynodes) are set to NA after Y=1 if survivalFunction is TRUE (or if specified by deterministic.Q.function).
 Your data did not conform and has been adjusted. This may be relevant if you are 
 writing your own deterministic function(s) or debugging ltmle.
Note: for internal purposes, all nodes after a censoring event are set to NA and 
 all nodes (except Ynodes) are set to NA after Y=1 if survivalFunction is TRUE (or if specified by deterministic.Q.function).
 Your data did not conform and has been adjusted. This may be relevant if you are 
 writing your own deterministic function(s) or debugging ltmle.
Note: for internal purposes, all nodes after a censoring event are set to NA and 
 all nodes (except Ynodes) are set to NA after Y=1 if survivalFunction is TRUE (or if specified by deterministic.Q.function).
 Your data did not conform and has been adjusted. This may be relevant if you are 
 writing your own deterministic function(s) or debugging ltmle.
Note: for internal purposes, all nodes after a censoring event are set to NA and 
 all nodes (except Ynodes) are set to NA after Y=1 if survivalFunction is TRUE (or if specified by deterministic.Q.function).
 Your data did not conform and has been adjusted. This may be relevant if you are 
 writing your own deterministic function(s) or debugging ltmle.
Note: for internal purposes, all nodes after a censoring event are set to NA and 
 all nodes (except Ynodes) are set to NA after Y=1 if survivalFunction is TRUE (or if specified by deterministic.Q.function).
 Your data did not conform and has been adjusted. This may be relevant if you are 
 writing your own deterministic function(s) or debugging ltmle.
Note: for internal purposes, all nodes after a censoring event are set to NA and 
 all nodes (except Ynodes) are set to NA after Y=1 if survivalFunction is TRUE (or if specified by deterministic.Q.function).
 Your data did not conform and has been adjusted. This may be relevant if you are 
 writing your own deterministic function(s) or debugging ltmle.
Note: for internal purposes, all nodes after a censoring event are set to NA and 
 all nodes (except Ynodes) are set to NA after Y=1 if survivalFunction is TRUE (or if specified by deterministic.Q.function).
 Your data did not conform and has been adjusted. This may be relevant if you are 
 writing your own deterministic function(s) or debugging ltmle.
Note: for internal purposes, all nodes after a censoring event are set to NA and 
 all nodes (except Ynodes) are set to NA after Y=1 if survivalFunction is TRUE (or if specified by deterministic.Q.function).
 Your data did not conform and has been adjusted. This may be relevant if you are 
 writing your own deterministic function(s) or debugging ltmle.
Some Ynodes are not in [0, 1], and Yrange was NULL, so all Y nodes are being
transformed to (Y-min.of.all.Ys)/range.of.all.Ys
summary(rltmle1)
Estimator:  tmle 
Call:
ltmle(data = ldata2, Anodes = c("a_0", "a_1", "a_2", "a_3", "a_4"), 
    Cnodes = c("c_0", "c_1", "c_2", "c_3", "c_4"), Lnodes = c("l_0", 
        "l_1", "l_2", "l_3", "l_4"), Ynodes = "y_4", survivalOutcome = FALSE, 
    Qform = qforma, gform = gforma, abar = list(c(1, 1, 1, 1, 
        1), c(0, 0, 0, 0, 0)), SL.library = SLlib, estimate.time = FALSE)

Treatment Estimate:
   Parameter Estimate:  3.8172 
    Estimated Std Err:  0.39059 
              p-value:  <2e-16 
    95% Conf Interval: (3.0517, 4.5828) 

Control Estimate:
   Parameter Estimate:  0.70144 
    Estimated Std Err:  0.19974 
              p-value:  <2e-16 
    95% Conf Interval: (0.30997, 1.0929) 

Additive Treatment Effect:
   Parameter Estimate:  3.1158 
    Estimated Std Err:  0.4358 
              p-value:  8.7083e-13 
    95% Conf Interval: (2.2616, 3.9699) 
# Outcome model misspecified
rltmle1m1 <- suppressMessages(suppressWarnings(ltmle(ldata2,
                                    Anodes=c("a_0","a_1","a_2","a_3","a_4"),
                                    Lnodes=c("l_0","l_1","l_2","l_3","l_4"),
                                    Cnodes=c("c_0","c_1","c_2","c_3","c_4"),
                                    Ynodes="y_4",
                                    abar=list(c(1,1,1,1,1),c(0,0,0,0,0)),
                                    SL.library=SLlib,
                                    Qform=mqforma,gform=gforma,
                                    estimate.time=FALSE,
                                    survivalOutcome=FALSE)))
summary(rltmle1m1)
Estimator:  tmle 
Call:
ltmle(data = ldata2, Anodes = c("a_0", "a_1", "a_2", "a_3", "a_4"), 
    Cnodes = c("c_0", "c_1", "c_2", "c_3", "c_4"), Lnodes = c("l_0", 
        "l_1", "l_2", "l_3", "l_4"), Ynodes = "y_4", survivalOutcome = FALSE, 
    Qform = mqforma, gform = gforma, abar = list(c(1, 1, 1, 1, 
        1), c(0, 0, 0, 0, 0)), SL.library = SLlib, estimate.time = FALSE)

Treatment Estimate:
   Parameter Estimate:  4.2545 
    Estimated Std Err:  0.37362 
              p-value:  <2e-16 
    95% Conf Interval: (3.5222, 4.9867) 

Control Estimate:
   Parameter Estimate:  0.8879 
    Estimated Std Err:  0.28458 
              p-value:  <2e-16 
    95% Conf Interval: (0.33013, 1.4457) 

Additive Treatment Effect:
   Parameter Estimate:  3.3666 
    Estimated Std Err:  0.4693 
              p-value:  7.3066e-13 
    95% Conf Interval: (2.4467, 4.2864) 
# Both models misspecified
rltmle1m2 <- suppressMessages(suppressWarnings(ltmle(ldata2,
                                    Anodes=c("a_0","a_1","a_2","a_3","a_4"),
                                    Lnodes=c("l_0","l_1","l_2","l_3","l_4"),
                                    Cnodes=c("c_0","c_1","c_2","c_3","c_4"),
                                    Ynodes="y_4",
                                    abar=list(c(1,1,1,1,1),c(0,0,0,0,0)),
                                    SL.library=SLlib,
                                    Qform=mqforma,gform=mgforma,
                                    estimate.time=FALSE,
                                    survivalOutcome=FALSE)))
summary(rltmle1m2)
Estimator:  tmle 
Call:
ltmle(data = ldata2, Anodes = c("a_0", "a_1", "a_2", "a_3", "a_4"), 
    Cnodes = c("c_0", "c_1", "c_2", "c_3", "c_4"), Lnodes = c("l_0", 
        "l_1", "l_2", "l_3", "l_4"), Ynodes = "y_4", survivalOutcome = FALSE, 
    Qform = mqforma, gform = mgforma, abar = list(c(1, 1, 1, 
        1, 1), c(0, 0, 0, 0, 0)), SL.library = SLlib, estimate.time = FALSE)

Treatment Estimate:
   Parameter Estimate:  4.7824 
    Estimated Std Err:  0.40755 
              p-value:  <2e-16 
    95% Conf Interval: (3.9836, 5.5812) 

Control Estimate:
   Parameter Estimate:  -0.31222 
    Estimated Std Err:  0.17067 
              p-value:  <2e-16 
    95% Conf Interval: (-0.64672, 0.022287) 

Additive Treatment Effect:
   Parameter Estimate:  5.0946 
    Estimated Std Err:  0.44143 
              p-value:  <2e-16 
    95% Conf Interval: (4.2294, 5.9598) 

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

slltmle1 <- suppressMessages(suppressWarnings(ltmle(ldata2,
                                   Anodes=c("a_0","a_1","a_2","a_3","a_4"),
                                   Lnodes=c("l_0","l_1","l_2","l_3","l_4"),
                                   Cnodes=c("c_0","c_1","c_2","c_3","c_4"),
                                   Ynodes="y_4",
                                   abar=list(c(1,1,1,1,1),c(0,0,0,0,0)),
                                   SL.library=SLlib3,
                                   estimate.time=FALSE,
                                   survivalOutcome=FALSE)))
summary(slltmle1)
Estimator:  tmle 
Call:
ltmle(data = ldata2, Anodes = c("a_0", "a_1", "a_2", "a_3", "a_4"), 
    Cnodes = c("c_0", "c_1", "c_2", "c_3", "c_4"), Lnodes = c("l_0", 
        "l_1", "l_2", "l_3", "l_4"), Ynodes = "y_4", survivalOutcome = FALSE, 
    abar = list(c(1, 1, 1, 1, 1), c(0, 0, 0, 0, 0)), SL.library = SLlib3, 
    estimate.time = FALSE)

Treatment Estimate:
   Parameter Estimate:  3.9612 
    Estimated Std Err:  0.28563 
              p-value:  <2e-16 
    95% Conf Interval: (3.4014, 4.5211) 

Control Estimate:
   Parameter Estimate:  0.72589 
    Estimated Std Err:  0.17506 
              p-value:  <2e-16 
    95% Conf Interval: (0.38277, 1.069) 

Additive Treatment Effect:
   Parameter Estimate:  3.2353 
    Estimated Std Err:  0.33118 
              p-value:  <2e-16 
    95% Conf Interval: (2.5862, 3.8844) 

Finally, for comparison purposes, we conduct naive analysis using generalised linear models:

# Correctly specified
LGLM <- glm(data=ldata,"y_4 ~ a_0 + a_1 + a_2 + a_3 + a_4 + l_0 + l_1 + l_2 + l_3 + l_4 + ba + bb + bc")
V1<-vcov(LGLM) # Save variance-covariance matrix to calculate joint standard error
# Incorrectly specified
LGLMm <- glm(data=ldata,"y_4 ~ a_0 + a_1 + a_2 + a_3 + a_4 + ba + bb + bc")
V2<-vcov(LGLMm) # Save variance-covariance matrix to calculate joint standard error

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

lresults1 <- matrix(c(coef(LGLM)[2]+coef(LGLM)[3]+coef(LGLM)[4]+coef(LGLM)[5]+coef(LGLM)[6],
                      V1[2,2] + V1[3,3] + V1[4,4] + V1[5,5] + V1[6,6]
                      + 2*V1[2,3]+ 2*V1[2,4] + 2*V1[2,5] + 2*V1[2,6]
                      + 2*V1[3,4] + 2*V1[3,5] + 2*V1[3,6]
                      + 2*V1[4,5] + 2*V1[4,6]
                      + 2*V1[5,6],
                      coef(LGLMm)[2]+coef(LGLMm)[3]+coef(LGLMm)[4]+coef(LGLMm)[5]+coef(LGLMm)[6],
                      V2[2,2] + V2[3,3] + V2[4,4] + V2[5,5] + V2[6,6]
                      + 2*V2[2,3]+ 2*V2[2,4] + 2*V2[2,5] + 2*V2[2,6]
                      + 2*V2[3,4] + 2*V2[3,5] + 2*V2[3,6]
                      + 2*V2[4,5] + 2*V2[4,6]
                      + 2*V2[5,6],
                      summary(rltmle1)$effect.measures$ATE$estimate,
                      summary(rltmle1)$effect.measures$ATE$std.dev,
                      summary(rltmle1m1)$effect.measures$ATE$estimate,
                      summary(rltmle1m1)$effect.measures$ATE$std.dev,
                      summary(rltmle1m2)$effect.measures$ATE$estimate,
                      summary(rltmle1m2)$effect.measures$ATE$std.dev,
                      summary(slltmle1)$effect.measures$ATE$estimate,
                      summary(slltmle1)$effect.measures$ATE$std.dev),nrow=6,ncol=2,byrow=TRUE)
rownames(lresults1) <- c("GLM - correctly specified","GLM - incorrectly specified","'ltmle' package - correctly specified","'ltmle' package - outcome misspecified","'ltmle' package - doubly misspecified","SuperLearner LTMLE")
colnames(lresults1) <- c("Coef","SE")
lresults1
                                           Coef        SE
GLM - correctly specified              2.410081 0.1136235
GLM - incorrectly specified            5.670451 0.2198963
'ltmle' package - correctly specified  3.115787 0.4358038
'ltmle' package - outcome misspecified 3.366560 0.4693005
'ltmle' package - doubly misspecified  5.094645 0.4414343
SuperLearner LTMLE                     3.235332 0.3311814
LS0tDQp0aXRsZTogIkxvbmdpdHVkaW5hbCBBbmFseXNpcyAtIFNpbmdsZSBPdXRjb21lIg0KZGF0ZTogIjMgRGVjZW1iZXIgMjAxOCINCm91dHB1dDogaHRtbF9ub3RlYm9vaw0KLS0tDQogIA0KYGBge3Igc2V0dXAsIGluY2x1ZGU9RkFMU0V9DQprbml0cjo6b3B0c19jaHVuayRzZXQoZWNobyA9IFRSVUUpDQpjbG91ZHN0b3IgPC0gIkM6L1VzZXJzL3ozMzEyOTExL0Nsb3Vkc3Rvci8iDQoubGliUGF0aHMocGFzdGUwKGNsb3Vkc3RvciwiUiBMaWJyYXJ5IikpDQoNCmxpYnJhcnkoInRtbGUiKQ0KbGlicmFyeSgibHRtbGUiKQ0KbGlicmFyeSgiU3VwZXJMZWFybmVyIikNCmxpYnJhcnkoInNpbWNhdXNhbCIpDQpsaWJyYXJ5KCJNQVNTIikNCmxpYnJhcnkoInJhbmdlciIpDQpsaWJyYXJ5KCJwYXJhbGxlbCIpDQpsaWJyYXJ5KCJkb1BhcmFsbGVsIikNCmxpYnJhcnkoImZvcmVhY2giKQ0KbGlicmFyeSgibG1lNCIpDQoNClJOR2tpbmQoa2luZD0iZGVmYXVsdCIsbm9ybWFsLmtpbmQ9ImRlZmF1bHQiKQ0Kc2V0LnNlZWQoNDMyMzYpDQpgYGANCg0KVGhpcyBjb2RlIHBlcmZvcm1zIGxvbmdpdHVkaW5hbCBhbmFseXNpcyB1c2luZyBib3RoIG5haXZlIGFuZCBUTUxFIGFuYWx5c2VzIG9uIHRoZSBmaXJzdCB3YXZlIG9mIHNpbXVsYXRlZCBkYXRhIGNyZWF0ZWQgaW4gdGhlIGRhdGEgY3JlYXRpb24gY29kZS4gDQpUaGUgY29kZSBwZXJmb3JtcyBhIHNlcmllcyBvZiBhbmFseXNlczoNCiAgDQoqIE5haXZlIGFuYWx5c2lzIHVzaW5nIEdMTXMNCiogQ29ycmVjdGx5IHNwZWNpZmllZCBMVE1MRSB1c2luZyBHTE1zIG9ubHkgaW4gdGhlIFIgcGFja2FnZSAnbHRtbGUnDQoqIFNpbmdseSBtaXNzcGVjaWZpZWQgKG91dGNvbWUgbW9kZWwgb25seSkgTFRNTEUgdXNpbmcgR0xNcyBvbmx5IGluIHRoZSBSIHBhY2thZ2UgJ2x0bWxlJw0KKiBEb3VibHkgbWlzc3BlY2lmaWVkIChib3RoIG1vZGVscykgTFRNTEUgdXNpbmcgR0xNcyBvbmx5IGluIHRoZSBSIHBhY2thZ2UgJ2x0bWxlJw0KKiAnQXV0b21hdGljJyBMVE1MRSB1c2luZyBTdXBlckxlYXJuZXIgaW4gdGhlIFIgcGFja2FnZSAndG1sZScNCg0KRmlyc3RseSwgd2UgZGVmaW5lIFN1cGVyTGVhcm5lciBsaWJyYXJpZXMgdG8gYmUgdXNlZCBieSBTdXBlckxlYXJuZXI6DQoNCmBgYHtyIHNsbGlifQ0KU0xsaWIgPC0gYygiU0wuZ2xtIikNClNMbGliMiA8LSBjKCJTTC5nbG0iLCJTTC5nbG0uaW50ZXJhY3Rpb24iLCJTTC5zdGVwQUlDIiwiU0wucmFuZ2VyIikNClNMbGliMyA8LSBsaXN0KFE9YygiU0wuZ2xtIiwiU0wuZ2xtLmludGVyYWN0aW9uIiwiU0wuc3RlcEFJQyIpLA0KICAgICAgICAgICAgICAgZz1jKCJTTC5nbG0iLCJTTC5nbG0uaW50ZXJhY3Rpb24iLCJTTC5zdGVwQUlDIiwiU0wucmFuZ2VyIikpDQpgYGANCg0KTmV4dCwgd2UgZGVmaW5lIHRoZSBtb2RlbHMgdG8gYmUgdXNlZCBieSAnbHRtbGUnIHdoZW4gbWFudWFsbHkgc3BlY2lmeWluZyBtb2RlbHMuDQonbHRtbGUnIHJlcXVpcmVzIG1vZGVscyB0byBiZSBkZWZpbmVkIGZvciBlYWNoIGV4cG9zdXJlIGFuZCBjZW5zb3JpbmcgdmFyaWFibGUgaW4gJ2dmb3JtJyBhbmQgdGhlIGZpcnN0IGluIGVhY2ggYmxvY2sgb2YgY29uZm91bmRlcnMgYW5kIGVhY2ggb3V0Y29tZSBpbiAncWZvcm0nLiBJbiB0aGlzIGNhc2UsIHRoZXJlIGlzIG9ubHkgb25lIG91dGNvbWUsIHNvIHFmb3JtIG9ubHkgY29udGFpbnMgb25lIG91dGNvbWUgbW9kZWwuDQonbHRtbGUnIGNhbiBhbHNvIHByb2R1Y2UgYSBzZXJpZXMgb2YgcSBhbmQgZyBtb2RlbHMgYXV0b21hdGljYWxseSBiYXNlZCBvbiB0aGUgZGF0YSAtIGlmIHFmb3JtIGFuZCBnZm9ybSBhcmUgbm90IHNwZWNpZmllZCBpbiB0aGUgY29tbWFuZCwgJ2x0bWxlJyB3aWxsIHByb2R1Y2UgYSBzZXQgb2YgYWxsIHJlcXVpcmVkIG1vZGVscywgdXNpbmcgYWxsIHByZWRpY3RvciB2YXJpYWJsZXMgdGhhdCBwcmVjZWVkIHRoYXQgdmFyaWFibGUgaW4gdGhlIGRhdGEuIEluIG1hbnkgY2FzZXMgdGhpcyBpcyBwZXJmZWN0bHkgYWNjZXB0YWJsZSAoaW4gdGhpcyBjYXNlLCB0aGUgY29ycmVjdGx5IHNwZWNpZmllZCBtb2RlbHMgYXJlIHRoZSBzYW1lIGFzIHRoZSBhdXRvbWF0aWNhbGx5IHByb2R1Y2UgbW9kZWxzKTsgaG93ZXZlciwgdGhlIG1vZGVscyBwcm9kdWNlZCBhdXRvbWF0aWNhbGx5IGNhbiBiZSBpbmNvcnJlY3Qgd2hlbiB2YXJpYWJsZXMgc2hvdWxkIG5vdCBiZSBpbmNsdWRlZCBpbiBzb21lIG9mIHRoZSBjb21wb25lbnQgbW9kZWxzIC0gZm9yIGV4YW1wbGUsIHdoZW4gcHJlZGljdG9ycyBvZiBjZW5zb3JpbmcgYXJlIG5vdCB0aGUgc2FtZSBhcyBwcmVkaWN0b3JzIG9mIGV4cG9zdXJlLg0KDQpgYGB7ciBmb3Jtc30NCiMgQ29ycmVjdGx5IHNwZWNpZmllZCBjb25mb3VuZGVyL291dGNvbWUgbW9kZWxzDQpxZm9ybWEgPC0gYyhsXzA9IlEua3BsdXMxIH4gYmEgKyBiYiArIGJjIiwNCiAgICAgICAgICAgIGxfMT0iUS5rcGx1czEgfiBiYSArIGJiICsgYmMgKyBsXzAgKyBhXzAiLA0KICAgICAgICAgICAgbF8yPSJRLmtwbHVzMSB+IGJhICsgYmIgKyBiYyArIGxfMCArIGFfMCArIGxfMSArIGFfMSIsDQogICAgICAgICAgICBsXzM9IlEua3BsdXMxIH4gYmEgKyBiYiArIGJjICsgbF8wICsgYV8wICsgbF8xICsgYV8xICsgbF8yICsgYV8yIiwNCiAgICAgICAgICAgIGxfND0iUS5rcGx1czEgfiBiYSArIGJiICsgYmMgKyBsXzAgKyBhXzAgKyBsXzEgKyBhXzEgKyBsXzIgKyBhXzIgKyBsXzMgKyBhXzMiLA0KICAgICAgICAgICAgeV80PSJRLmtwbHVzMSB+IGJhICsgYmIgKyBiYyArIGxfMCArIGFfMCArIGxfMSArIGFfMSArIGxfMiArIGFfMiArIGxfMyArIGFfMyArIGxfNCArIGFfNCIpDQojIEluY29ycmVjdGx5IHNwZWNpZmllZCBjb25mb3VuZGVyL291dGNvbWUgbW9kZWxzDQptcWZvcm1hIDwtIGMobF8wPSJRLmtwbHVzMSB+IGJhICsgYmIgKyBiYyIsDQogICAgICAgICAgICAgbF8xPSJRLmtwbHVzMSB+IGJhICsgYmIgKyBiYyArIGFfMCIsDQogICAgICAgICAgICAgbF8yPSJRLmtwbHVzMSB+IGJhICsgYmIgKyBiYyArIGFfMCArIGFfMSIsDQogICAgICAgICAgICAgbF8zPSJRLmtwbHVzMSB+IGJhICsgYmIgKyBiYyArIGFfMCArIGFfMSArIGFfMiIsDQogICAgICAgICAgICAgbF80PSJRLmtwbHVzMSB+IGJhICsgYmIgKyBiYyArIGFfMCArIGFfMSArIGFfMiArIGFfMyIsDQogICAgICAgICAgICAgeV80PSJRLmtwbHVzMSB+IGJhICsgYmIgKyBiYyArIGFfMCArIGFfMSArIGFfMiArIGFfMyArIGFfNCIpDQojIENvcnJlY3RseSBzcGVjaWZpZWQgZXhwb3N1cmUvY2Vuc29yaW5nIG1vZGVscw0KZ2Zvcm1hIDwtIGMoY18wPSJjXzAgfiBiYSArIGJiICsgYmMiLA0KICAgICAgICAgICAgYV8wPSJhXzAgfiBiYSArIGJiICsgYmMgKyBsXzAiLA0KICAgICAgICAgICAgY18xPSJjXzEgfiBiYSArIGJiICsgYmMgKyBsXzAgKyBhXzAgIiwNCiAgICAgICAgICAgIGFfMT0iYV8xIH4gYmEgKyBiYiArIGJjICsgbF8wICsgYV8wICsgbF8xIiwNCiAgICAgICAgICAgIGNfMj0iY18yIH4gYmEgKyBiYiArIGJjICsgbF8wICsgYV8wICsgbF8xICsgYV8xIiwNCiAgICAgICAgICAgIGFfMj0iYV8yIH4gYmEgKyBiYiArIGJjICsgbF8wICsgYV8wICsgbF8xICsgYV8xICsgbF8yIiwNCiAgICAgICAgICAgIGNfMz0iY18zIH4gYmEgKyBiYiArIGJjICsgbF8wICsgYV8wICsgbF8xICsgYV8xICsgbF8yICsgYV8yIiwNCiAgICAgICAgICAgIGFfMz0iYV8zIH4gYmEgKyBiYiArIGJjICsgbF8wICsgYV8wICsgbF8xICsgYV8xICsgbF8yICsgYV8yICsgbF8zIiwNCiAgICAgICAgICAgIGNfND0iY180IH4gYmEgKyBiYiArIGJjICsgbF8wICsgYV8wICsgbF8xICsgYV8xICsgbF8yICsgYV8yICsgbF8zICsgYV8zIiwNCiAgICAgICAgICAgIGFfND0iYV80IH4gYmEgKyBiYiArIGJjICsgbF8wICsgYV8wICsgbF8xICsgYV8xICsgbF8yICsgYV8yICsgbF8zICsgYV8zICsgbF80IikNCiMgSW5jb3JyZWN0bHkgc3BlY2lmaWVkIGV4cG9zdXJlL2NlbnNvcmluZyBtb2RlbHMNCm1nZm9ybWEgPC0gYyhjXzA9ImNfMCB+IGJhICsgYmIgKyBiYyIsDQogICAgICAgICAgICAgYV8wPSJhXzAgfiBiYSArIGJiICsgYmMiLA0KICAgICAgICAgICAgIGNfMT0iY18xIH4gYmEgKyBiYiArIGJjICsgYV8wIiwNCiAgICAgICAgICAgICBhXzE9ImFfMSB+IGJhICsgYmIgKyBiYyArIGFfMCIsDQogICAgICAgICAgICAgY18yPSJjXzIgfiBiYSArIGJiICsgYmMgKyBhXzAgKyBhXzEiLA0KICAgICAgICAgICAgIGFfMj0iYV8yIH4gYmEgKyBiYiArIGJjICsgYV8wICsgYV8xIiwNCiAgICAgICAgICAgICBjXzM9ImNfMyB+IGJhICsgYmIgKyBiYyArIGFfMCArIGFfMSArIGFfMiIsDQogICAgICAgICAgICAgYV8zPSJhXzMgfiBiYSArIGJiICsgYmMgKyBhXzAgKyBhXzEgKyBhXzIiLA0KICAgICAgICAgICAgIGNfND0iY180IH4gYmEgKyBiYiArIGJjICsgYV8wICsgYV8xICsgYV8yICsgYV8zIiwNCiAgICAgICAgICAgICBhXzQ9ImFfNCB+IGJhICsgYmIgKyBiYyArIGFfMCArIGFfMSArIGFfMiArIGFfMyIpDQpgYGANCg0KTm93LCB3ZSBjcmVhdGUgYSBkYXRhIHN1YnNldCB3aXRoIGFsbCBvYnNlcnZhdGlvbnMgb2YgZXhwb3N1cmUgYW5kIGNvbmZvdW5kZXJzLCBidXQgb25seSBmaW5hbCBvdXRjb21lIFk0Og0KDQpgYGB7ciBkYXRhfQ0KbGRhdGEyIDwtIGxkYXRhWyxjKC0xLC04LC0xMiwtMTYsLTIwKV0NCmBgYA0KDQpOb3cgd2UgY2FuIGJlZ2luIGFuYWx5c2lzLiBGaXJzdGx5LCB3ZSBydW4gVE1MRSB1c2luZyB0aGUgJ2x0bWxlJyBwYWNrYWdlLCBidXQgbWFudWFsbHkgc3BlY2lmeWluZyB0aGUgbW9kZWxzIHRvIGJlIHVzZWQgKGFzIGRlZmluZWQgYWJvdmUpLCBhbmQgd2l0aCBlc3RpbWF0aW9uIGNvbmR1Y3RlZCB1c2luZyBvbmx5IGdlbmVyYWxpc2VkIGxpbmVhciBtb2RlbHMuDQpOb3RlIHRoYXQsIGJlY2F1c2UgJ2x0bWxlJyBjaGVja3MgYW5kIHRyYW5zZm9ybXMgY29udGludW91cyBvdXRjb21lcywgYW5kIGNoZWNrcyB0aGF0IGRhdGEgaXMgYWx3YXlzIG1pc3NpbmcgYWZ0ZXIgY2Vuc29yaW5nLiBGb3IgY29udGludW91cyBvdXRjb21lcywgdGhlIHZhcmlhYmxlIGlzIHRydW5jYXRlZCB0byBhIHF1YXNpYmlub21pYWwgZGlzdHJpYnV0aW9uIChjb250aW5vdXMgYnV0IGJvdW5kZWQgaW4gMC8xKTsgZm9yIG1pc3NpbmcgZGF0YSwgYW5kIG9ic2VydmF0aW9ucyBhZnRlciBhIGNlbnNvcmluZyBldmVudCBhcmUgaWdub3JlZC4gQmVjYXVzZSBvZiB0aGVzZSBjaGVja3MsIHRoZSBjb21tYW5kIHBvdGVudGlhbGx5IHByb2R1Y2VzIGEgbnVtYmVyIG9mIG1lc3NhZ2VzLiBUaGVzZSBhcmUgIG5vdCBhbiBpc3N1ZSwgYW5kIGhhdmUgYmVlbiBsZWZ0IGVuYWJsZWQgZm9yIHRoZSBmaXJzdCBhbmFseXNpcyB0byBzaG93IHdoYXQgdGhleSBsb29rIGxpa2UsIGJ1dCBoYXZlIGJlZW4gZGlzYWJsZWQgaW4gc3Vic2VxdWVudCBhbmFseXNlcyB0byBzaW1wbGlmeSB0aGlzIG1hcmtkb3duIGRvY3VtZW50Lg0KDQpgYGB7ciBtYW51YWx9DQojIENvcnJlY3RseSBzcGVjaWZpZWQNCnJsdG1sZTEgPC0gc3VwcHJlc3NXYXJuaW5ncyhsdG1sZShsZGF0YTIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgQW5vZGVzPWMoImFfMCIsImFfMSIsImFfMiIsImFfMyIsImFfNCIpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIExub2Rlcz1jKCJsXzAiLCJsXzEiLCJsXzIiLCJsXzMiLCJsXzQiKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBDbm9kZXM9YygiY18wIiwiY18xIiwiY18yIiwiY18zIiwiY180IiksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgWW5vZGVzPSJ5XzQiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFiYXI9bGlzdChjKDEsMSwxLDEsMSksYygwLDAsMCwwLDApKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBTTC5saWJyYXJ5PVNMbGliLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFFmb3JtPXFmb3JtYSxnZm9ybT1nZm9ybWEsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZXN0aW1hdGUudGltZT1GQUxTRSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzdXJ2aXZhbE91dGNvbWU9RkFMU0UpKQ0Kc3VtbWFyeShybHRtbGUxKQ0KIyBPdXRjb21lIG1vZGVsIG1pc3NwZWNpZmllZA0Kcmx0bWxlMW0xIDwtIHN1cHByZXNzTWVzc2FnZXMoc3VwcHJlc3NXYXJuaW5ncyhsdG1sZShsZGF0YTIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBBbm9kZXM9YygiYV8wIiwiYV8xIiwiYV8yIiwiYV8zIiwiYV80IiksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBMbm9kZXM9YygibF8wIiwibF8xIiwibF8yIiwibF8zIiwibF80IiksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBDbm9kZXM9YygiY18wIiwiY18xIiwiY18yIiwiY18zIiwiY180IiksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBZbm9kZXM9InlfNCIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhYmFyPWxpc3QoYygxLDEsMSwxLDEpLGMoMCwwLDAsMCwwKSksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBTTC5saWJyYXJ5PVNMbGliLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgUWZvcm09bXFmb3JtYSxnZm9ybT1nZm9ybWEsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBlc3RpbWF0ZS50aW1lPUZBTFNFLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc3Vydml2YWxPdXRjb21lPUZBTFNFKSkpDQpzdW1tYXJ5KHJsdG1sZTFtMSkNCiMgQm90aCBtb2RlbHMgbWlzc3BlY2lmaWVkDQpybHRtbGUxbTIgPC0gc3VwcHJlc3NNZXNzYWdlcyhzdXBwcmVzc1dhcm5pbmdzKGx0bWxlKGxkYXRhMiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIEFub2Rlcz1jKCJhXzAiLCJhXzEiLCJhXzIiLCJhXzMiLCJhXzQiKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIExub2Rlcz1jKCJsXzAiLCJsXzEiLCJsXzIiLCJsXzMiLCJsXzQiKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIENub2Rlcz1jKCJjXzAiLCJjXzEiLCJjXzIiLCJjXzMiLCJjXzQiKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFlub2Rlcz0ieV80IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFiYXI9bGlzdChjKDEsMSwxLDEsMSksYygwLDAsMCwwLDApKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFNMLmxpYnJhcnk9U0xsaWIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBRZm9ybT1tcWZvcm1hLGdmb3JtPW1nZm9ybWEsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBlc3RpbWF0ZS50aW1lPUZBTFNFLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc3Vydml2YWxPdXRjb21lPUZBTFNFKSkpDQpzdW1tYXJ5KHJsdG1sZTFtMikNCmBgYA0KDQpOZXh0LCB3ZSBjYXJyeSBvdXQgYW5hbHlzaXMgdXNpbmcgU3VwZXJMZWFybmVyLCBhbGxvd2luZyAnbHRtbGUnIHRvIGRlZmluZSB0aGUgaW50ZXJuYWwgbW9kZWxzOg0KDQpgYGB7ciBhdXRvc2x9DQpzbGx0bWxlMSA8LSBzdXBwcmVzc01lc3NhZ2VzKHN1cHByZXNzV2FybmluZ3MobHRtbGUobGRhdGEyLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBBbm9kZXM9YygiYV8wIiwiYV8xIiwiYV8yIiwiYV8zIiwiYV80IiksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIExub2Rlcz1jKCJsXzAiLCJsXzEiLCJsXzIiLCJsXzMiLCJsXzQiKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgQ25vZGVzPWMoImNfMCIsImNfMSIsImNfMiIsImNfMyIsImNfNCIpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBZbm9kZXM9InlfNCIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFiYXI9bGlzdChjKDEsMSwxLDEsMSksYygwLDAsMCwwLDApKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgU0wubGlicmFyeT1TTGxpYjMsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGVzdGltYXRlLnRpbWU9RkFMU0UsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHN1cnZpdmFsT3V0Y29tZT1GQUxTRSkpKQ0Kc3VtbWFyeShzbGx0bWxlMSkNCmBgYA0KDQpGaW5hbGx5LCBmb3IgY29tcGFyaXNvbiBwdXJwb3Nlcywgd2UgY29uZHVjdCBuYWl2ZSBhbmFseXNpcyB1c2luZyBnZW5lcmFsaXNlZCBsaW5lYXIgbW9kZWxzOg0KDQpgYGB7ciBuYWl2ZX0NCiMgQ29ycmVjdGx5IHNwZWNpZmllZA0KTEdMTSA8LSBnbG0oZGF0YT1sZGF0YSwieV80IH4gYV8wICsgYV8xICsgYV8yICsgYV8zICsgYV80ICsgbF8wICsgbF8xICsgbF8yICsgbF8zICsgbF80ICsgYmEgKyBiYiArIGJjIikNClYxPC12Y292KExHTE0pICMgU2F2ZSB2YXJpYW5jZS1jb3ZhcmlhbmNlIG1hdHJpeCB0byBjYWxjdWxhdGUgam9pbnQgc3RhbmRhcmQgZXJyb3INCiMgSW5jb3JyZWN0bHkgc3BlY2lmaWVkDQpMR0xNbSA8LSBnbG0oZGF0YT1sZGF0YSwieV80IH4gYV8wICsgYV8xICsgYV8yICsgYV8zICsgYV80ICsgYmEgKyBiYiArIGJjIikNClYyPC12Y292KExHTE1tKSAjIFNhdmUgdmFyaWFuY2UtY292YXJpYW5jZSBtYXRyaXggdG8gY2FsY3VsYXRlIGpvaW50IHN0YW5kYXJkIGVycm9yDQpgYGANCg0KTGV0cyBzZWUgYSBzdW1tYXJ5IG9mIHRoZSByZXN1bHRzIHByb2R1Y2VkIGJ5IGVhY2ggb2YgdGhlIG1ldGhvZHMsIHNvIHdlIGNhbiBjb21wYXJlIHRoZW06DQoNCmBgYHtyIHN1bW1hcnl9DQpscmVzdWx0czEgPC0gbWF0cml4KGMoY29lZihMR0xNKVsyXStjb2VmKExHTE0pWzNdK2NvZWYoTEdMTSlbNF0rY29lZihMR0xNKVs1XStjb2VmKExHTE0pWzZdLA0KICAgICAgICAgICAgICAgICAgICAgIFYxWzIsMl0gKyBWMVszLDNdICsgVjFbNCw0XSArIFYxWzUsNV0gKyBWMVs2LDZdDQogICAgICAgICAgICAgICAgICAgICAgKyAyKlYxWzIsM10rIDIqVjFbMiw0XSArIDIqVjFbMiw1XSArIDIqVjFbMiw2XQ0KICAgICAgICAgICAgICAgICAgICAgICsgMipWMVszLDRdICsgMipWMVszLDVdICsgMipWMVszLDZdDQogICAgICAgICAgICAgICAgICAgICAgKyAyKlYxWzQsNV0gKyAyKlYxWzQsNl0NCiAgICAgICAgICAgICAgICAgICAgICArIDIqVjFbNSw2XSwNCiAgICAgICAgICAgICAgICAgICAgICBjb2VmKExHTE1tKVsyXStjb2VmKExHTE1tKVszXStjb2VmKExHTE1tKVs0XStjb2VmKExHTE1tKVs1XStjb2VmKExHTE1tKVs2XSwNCiAgICAgICAgICAgICAgICAgICAgICBWMlsyLDJdICsgVjJbMywzXSArIFYyWzQsNF0gKyBWMls1LDVdICsgVjJbNiw2XQ0KICAgICAgICAgICAgICAgICAgICAgICsgMipWMlsyLDNdKyAyKlYyWzIsNF0gKyAyKlYyWzIsNV0gKyAyKlYyWzIsNl0NCiAgICAgICAgICAgICAgICAgICAgICArIDIqVjJbMyw0XSArIDIqVjJbMyw1XSArIDIqVjJbMyw2XQ0KICAgICAgICAgICAgICAgICAgICAgICsgMipWMls0LDVdICsgMipWMls0LDZdDQogICAgICAgICAgICAgICAgICAgICAgKyAyKlYyWzUsNl0sDQogICAgICAgICAgICAgICAgICAgICAgc3VtbWFyeShybHRtbGUxKSRlZmZlY3QubWVhc3VyZXMkQVRFJGVzdGltYXRlLA0KICAgICAgICAgICAgICAgICAgICAgIHN1bW1hcnkocmx0bWxlMSkkZWZmZWN0Lm1lYXN1cmVzJEFURSRzdGQuZGV2LA0KICAgICAgICAgICAgICAgICAgICAgIHN1bW1hcnkocmx0bWxlMW0xKSRlZmZlY3QubWVhc3VyZXMkQVRFJGVzdGltYXRlLA0KICAgICAgICAgICAgICAgICAgICAgIHN1bW1hcnkocmx0bWxlMW0xKSRlZmZlY3QubWVhc3VyZXMkQVRFJHN0ZC5kZXYsDQogICAgICAgICAgICAgICAgICAgICAgc3VtbWFyeShybHRtbGUxbTIpJGVmZmVjdC5tZWFzdXJlcyRBVEUkZXN0aW1hdGUsDQogICAgICAgICAgICAgICAgICAgICAgc3VtbWFyeShybHRtbGUxbTIpJGVmZmVjdC5tZWFzdXJlcyRBVEUkc3RkLmRldiwNCiAgICAgICAgICAgICAgICAgICAgICBzdW1tYXJ5KHNsbHRtbGUxKSRlZmZlY3QubWVhc3VyZXMkQVRFJGVzdGltYXRlLA0KICAgICAgICAgICAgICAgICAgICAgIHN1bW1hcnkoc2xsdG1sZTEpJGVmZmVjdC5tZWFzdXJlcyRBVEUkc3RkLmRldiksbnJvdz02LG5jb2w9MixieXJvdz1UUlVFKQ0Kcm93bmFtZXMobHJlc3VsdHMxKSA8LSBjKCJHTE0gLSBjb3JyZWN0bHkgc3BlY2lmaWVkIiwiR0xNIC0gaW5jb3JyZWN0bHkgc3BlY2lmaWVkIiwiJ2x0bWxlJyBwYWNrYWdlIC0gY29ycmVjdGx5IHNwZWNpZmllZCIsIidsdG1sZScgcGFja2FnZSAtIG91dGNvbWUgbWlzc3BlY2lmaWVkIiwiJ2x0bWxlJyBwYWNrYWdlIC0gZG91Ymx5IG1pc3NwZWNpZmllZCIsIlN1cGVyTGVhcm5lciBMVE1MRSIpDQpjb2xuYW1lcyhscmVzdWx0czEpIDwtIGMoIkNvZWYiLCJTRSIpDQpscmVzdWx0czENCmBgYA==