The follorwing is a collection of notes based on Googles reseach and practical application of Causal Inference. This demonstration is conducted in an R Notebook using the R package CausalImpact created by Google for this purpose.

Installing the package

To install CausalImpact, type the following commands into an R session:

install.packages("devtools")
library(devtools)
devtools::install_github("google/CausalImpact")

Note: You can now use

install.packages("CausalImpact")
library(bsts) # required for Causal Impact
library(CausalImpact)

Once installed, the package can be loaded in a given R session using:

library(CausalImpact)

Creating an example dataset

To illustrate how the package works, we create a simple toy dataset. It consists of a response variable y and a predictor x1. Note that in practice, we’d strive for including many more predictor variables and let the model choose an appropriate subset. The example data has 100 observations. We create an intervention effect by lifting the response variable by 10 units after timepoint 71.

set.seed(1)
x1 <- 100 + arima.sim(model = list(ar = 0.999), n = 100)
y <- 1.2 * x1 + rnorm(100)
y[71:100] <- y[71:100] + 10
data <- cbind(y, x1)

We now have a simple matrix with 100 rows and two columns:

dim(data)
[1] 100   2

[1] 100 2

head(data)
            y       x1
[1,] 105.2950 88.21513
[2,] 105.8943 88.48415
[3,] 106.6209 87.87684
[4,] 106.1572 86.77954
[5,] 101.2812 84.62243
[6,] 101.4484 84.60650

We can visualize the generated data using:

matplot(data, type = "l")

Running an analysis

To estimate a causal effect, we begin by specifying which period in the data should be used for training the model (pre-intervention period) and which period for computing a counterfactual prediction (post-intervention period).

pre.period <- c(1, 70)
post.period <- c(71, 100)

This says that time points 1 … 70 will be used for training, and time points 71 … 100 will be used for computing predictions. Alternatively, we could specify the periods in terms of dates or time points; see Section 5 for an example.

To perform inference, we run the analysis using:

impact <- CausalImpact(data, pre.period, post.period)

This instructs the package to assemble a structural time-series model, perform posterior inference, and compute estimates of the causal effect. The return value is a CausalImpact object.

Plotting the results

The easiest way of visualizing the results is to use the plot() function that is part of the package:

plot(impact)

By default, the plot contains three panels. The first panel shows the data and a counterfactual prediction for the post-treatment period. The second panel shows the difference between observed data and counterfactual predictions. This is the pointwise causal effect, as estimated by the model. The third panel adds up the pointwise contributions from the second panel, resulting in a plot of the cumulative effect of the intervention.

Remember, once again, that all of the above inferences depend critically on the assumption that the covariates were not themselves affected by the intervention. The model also assumes that the relationship between covariates and treated time series, as established during the pre-period, remains stable throughout the post-period.

  1. Working with dates and times

It is often more natural to feed a time-series object into CausalImpact() rather than a data frame. For example, we might create a data variable as follows:

time.points <- seq.Date(as.Date("2014-01-01"), by = 1, length.out = 100)
data <- zoo(cbind(y, x1), time.points)
head(data)
                  y       x1
2014-01-01 105.2950 88.21513
2014-01-02 105.8943 88.48415
2014-01-03 106.6209 87.87684
2014-01-04 106.1572 86.77954
2014-01-05 101.2812 84.62243
2014-01-06 101.4484 84.60650

We can now specify the pre-period and the post-period in terms of time points rather than indices:

pre.period <- as.Date(c("2014-01-01", "2014-03-11"))
post.period <- as.Date(c("2014-03-12", "2014-04-10"))

As a result, the x-axis of the plot shows time points instead of indices:

impact <- CausalImpact(data, pre.period, post.period)
plot(impact)

Printing a summary table

To obtain a numerical summary of the analysis, we use:

summary(impact)
Posterior inference {CausalImpact}

                         Average        Cumulative  
Actual                   117            3511        
Prediction (s.d.)        107 (0.37)     3196 (11.02)
95% CI                   [106, 107]     [3174, 3217]
                                                    
Absolute effect (s.d.)   11 (0.37)      315 (11.02) 
95% CI                   [9.8, 11]      [294.4, 338]
                                                    
Relative effect (s.d.)   9.9% (0.34%)   9.9% (0.34%)
95% CI                   [9.2%, 11%]    [9.2%, 11%] 

Posterior tail-area probability p:   0.001
Posterior prob. of a causal effect:  99.9%

For more details, type: summary(impact, "report")

The Average column talks about the average (across time) during the post-intervention period (in the example: time points 71 through 100). The Cumulative column sums up individual time points, which is a useful perspective if the response variable represents a flow quantity (such as queries, clicks, visits, installs, sales, or revenue) rather than a stock quantity (such as number of users or stock price).

In the example, the estimated average causal effect of treatment was 11 (rounded to a whole number; for full precision see impact$summary). This is because we observed an average value of 99 but would have expected an average value of only 89. The 95% posterior interval of the average effect is [9.8, 11]. Since this excludes 0, we (correctly) conclude that the intervention had a causal effect on the response variable. Since we generated the data ourselves, we know that we injected a true effect of 10, and so the model accurately recovered ground truth. One reason for this is that we ensured, by design, that the covariate x1 was not itself affected by the intervention. In practice, we must always reason whether this assumption is justified.

For additional guidance about the correct interpretation of the summary table, the package provides a verbal interpretation, which we can print using:

summary(impact, "report")
Analysis report {CausalImpact}


During the post-intervention period, the response variable had an average value of approx. 117.05. By contrast, in the absence of an intervention, we would have expected an average response of 106.54. The 95% interval of this counterfactual prediction is [105.80, 107.24]. Subtracting this prediction from the observed response yields an estimate of the causal effect the intervention had on the response variable. This effect is 10.51 with a 95% interval of [9.81, 11.25]. For a discussion of the significance of this effect, see below.

Summing up the individual data points during the post-intervention period (which can only sometimes be meaningfully interpreted), the response variable had an overall value of 3.51K. By contrast, had the intervention not taken place, we would have expected a sum of 3.20K. The 95% interval of this prediction is [3.17K, 3.22K].

The above results are given in terms of absolute numbers. In relative terms, the response variable showed an increase of +10%. The 95% interval of this percentage is [+9%, +11%].

This means that the positive effect observed during the intervention period is statistically significant and unlikely to be due to random fluctuations. It should be noted, however, that the question of whether this increase also bears substantive significance can only be answered by comparing the absolute effect (10.51) to the original goal of the underlying intervention.

The probability of obtaining this effect by chance is very small (Bayesian one-sided tail-area probability p = 0.001). This means the causal effect can be considered statistically significant. 

The individual numbers in the table, at full precision, can be accessed using:

impact$summary

See below for tips on how to use these commands with knitr / R Markdown.

Adjusting the model

So far, we’ve simply let the package decide how to construct a time-series model for the available data. However, there are several options that allow us to gain a little more control over this process. These options are passed into model.args as individual list elements, for example:

impact <- CausalImpact(..., model.args = list(niter = 5000, nseasons = 7))

Available options

  • niter Number of MCMC samples to draw. More samples lead to more accurate inferences. Defaults to 1000.
  • standardize.data Whether to standardize all columns of the data before fitting the model. This is equivalent to an empirical Bayes approach to setting the priors. It ensures that results are invariant to linear transformations of the data. Defaults to TRUE.
  • prior.level.sd Prior standard deviation of the Gaussian random walk of the local level. Expressed in terms of data standard deviations. Defaults to 0.01, a typical choice for well-behaved and stable datasets with low residual volatility after regressing out known predictors (e.g., web searches or sales in high quantities). When in doubt, a safer option is to use 0.1, as validated on synthetic data, although this may sometimes give rise to unrealistically wide prediction intervals.
  • nseasons Period of the seasonal components. In order to include a seasonal component, set this to a whole number greater than 1. For example, if the data represent daily observations, use 7 for a day-of-week component. This interface currently only supports up to one seasonal component. To specify multiple seasonal components, use bsts to specify the model directly, then pass the fitted model in as bsts.model. Defaults to 1, which means no seasonal component is used.
  • season.duration Duration of each season, i.e., number of data points each season spans. Defaults to 1. For example, to add a day-of-week component to data with daily granularity, use model.args = list(nseasons = 7, season.duration = 1). To add a day-of-week component to data with hourly granularity, set model.args = list(nseasons = 7, season.duration = 24).
  • dynamic.regression Whether to include time-varying regression coefficients. In combination with a time-varying local trend or even a time-varying local level, this often leads to overspecification, in which case a static regression is safer. Defaults to FALSE.

Using a custom model

Instead of using the default model constructed by the CausalImpact package, we can use the bsts package to specify our own model. This provides the greatest degree of flexibility.

Before constructing a custom model, we set the observed data in the post-treatment period to NA, reflecting the fact that the counterfactual response is unobserved after the intervention. We keep a copy of the actual observed response in the variable post.period.response.

post.period <- c(71, 100)
post.period.response <- y[post.period[1] : post.period[2]]
y[post.period[1] : post.period[2]] <- NA

We next set up and estimate a time-series model using the bsts package. Here is a simple example:

ss <- AddLocalLevel(list(), y)
bsts.model <- bsts(y ~ x1, ss, niter = 1000)
=-=-=-=-= Iteration 0 Mon May  8 14:40:52 2017 =-=-=-=-=
=-=-=-=-= Iteration 100 Mon May  8 14:40:52 2017 =-=-=-=-=
=-=-=-=-= Iteration 200 Mon May  8 14:40:52 2017 =-=-=-=-=
=-=-=-=-= Iteration 300 Mon May  8 14:40:52 2017 =-=-=-=-=
=-=-=-=-= Iteration 400 Mon May  8 14:40:53 2017 =-=-=-=-=
=-=-=-=-= Iteration 500 Mon May  8 14:40:53 2017 =-=-=-=-=
=-=-=-=-= Iteration 600 Mon May  8 14:40:53 2017 =-=-=-=-=
=-=-=-=-= Iteration 700 Mon May  8 14:40:53 2017 =-=-=-=-=
=-=-=-=-= Iteration 800 Mon May  8 14:40:53 2017 =-=-=-=-=
=-=-=-=-= Iteration 900 Mon May  8 14:40:53 2017 =-=-=-=-=

Finally, we call CausalImpact Instead of providing input data, we simply pass in the fitted model object (bsts.model). We also need to provide the actual observed response. This is needed so that the package can compute the difference between predicted response (stored in bsts.model) and actual observed response (stored in post.period.response).

impact <- CausalImpact(bsts.model = bsts.model,
                       post.period.response = post.period.response)

The results can be inspected in the usual way:

plot(impact)

summary(impact)
Posterior inference {CausalImpact}

                         Average       Cumulative  
Actual                   117           3511        
Prediction (s.d.)        106 (0.49)    3190 (14.59)
95% CI                   [105, 107]    [3162, 3218]
                                                   
Absolute effect (s.d.)   11 (0.49)     321 (14.59) 
95% CI                   [9.8, 12]     [293.4, 350]
                                                   
Relative effect (s.d.)   10% (0.46%)   10% (0.46%) 
95% CI                   [9.2%, 11%]   [9.2%, 11%] 

Posterior tail-area probability p:   0.00105
Posterior prob. of a causal effect:  99.8954%

For more details, type: summary(impact, "report")
summary(impact, "report")
Analysis report {CausalImpact}


During the post-intervention period, the response variable had an average value of approx. 117.05. By contrast, in the absence of an intervention, we would have expected an average response of 106.34. The 95% interval of this counterfactual prediction is [105.39, 107.27]. Subtracting this prediction from the observed response yields an estimate of the causal effect the intervention had on the response variable. This effect is 10.71 with a 95% interval of [9.78, 11.65]. For a discussion of the significance of this effect, see below.

Summing up the individual data points during the post-intervention period (which can only sometimes be meaningfully interpreted), the response variable had an overall value of 3.51K. By contrast, had the intervention not taken place, we would have expected a sum of 3.19K. The 95% interval of this prediction is [3.16K, 3.22K].

The above results are given in terms of absolute numbers. In relative terms, the response variable showed an increase of +10%. The 95% interval of this percentage is [+9%, +11%].

This means that the positive effect observed during the intervention period is statistically significant and unlikely to be due to random fluctuations. It should be noted, however, that the question of whether this increase also bears substantive significance can only be answered by comparing the absolute effect (10.71) to the original goal of the underlying intervention.

The probability of obtaining this effect by chance is very small (Bayesian one-sided tail-area probability p = 0.001). This means the causal effect can be considered statistically significant. 
LS0tCnRpdGxlOiAiQ2FzdWFsIEltcGFjdCBOb3RlcyIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQoKVGhlIGZvbGxvcndpbmcgaXMgYSBjb2xsZWN0aW9uIG9mIG5vdGVzIGJhc2VkIG9uIEdvb2dsZXMgcmVzZWFjaCBhbmQgcHJhY3RpY2FsIGFwcGxpY2F0aW9uIG9mIENhdXNhbCBJbmZlcmVuY2UuIFRoaXMgZGVtb25zdHJhdGlvbiBpcyBjb25kdWN0ZWQgaW4gYW4gUiBOb3RlYm9vayB1c2luZyB0aGUgUiBwYWNrYWdlIGBDYXVzYWxJbXBhY3RgIGNyZWF0ZWQgYnkgR29vZ2xlIGZvciB0aGlzIHB1cnBvc2UuCgojIyBJbnN0YWxsaW5nIHRoZSBwYWNrYWdlCgpUbyBpbnN0YWxsIGBDYXVzYWxJbXBhY3RgLCB0eXBlIHRoZSBmb2xsb3dpbmcgY29tbWFuZHMgaW50byBhbiBSIHNlc3Npb246CgpgYGB7ciwgZXZhbD1GQUxTRX0KaW5zdGFsbC5wYWNrYWdlcygiZGV2dG9vbHMiKQpsaWJyYXJ5KGRldnRvb2xzKQpkZXZ0b29sczo6aW5zdGFsbF9naXRodWIoImdvb2dsZS9DYXVzYWxJbXBhY3QiKQpgYGAKTm90ZTogWW91IGNhbiBub3cgdXNlCmBgYHtyLCBldmFsPUZBTFNFfQppbnN0YWxsLnBhY2thZ2VzKCJDYXVzYWxJbXBhY3QiKQpgYGAKCmBgYHtyfQpsaWJyYXJ5KGJzdHMpICMgcmVxdWlyZWQgZm9yIENhdXNhbCBJbXBhY3QKbGlicmFyeShDYXVzYWxJbXBhY3QpCmBgYAoKT25jZSBpbnN0YWxsZWQsIHRoZSBwYWNrYWdlIGNhbiBiZSBsb2FkZWQgaW4gYSBnaXZlbiBSIHNlc3Npb24gdXNpbmc6CmBgYHtyfQpsaWJyYXJ5KENhdXNhbEltcGFjdCkKYGBgCgojIyBDcmVhdGluZyBhbiBleGFtcGxlIGRhdGFzZXQKClRvIGlsbHVzdHJhdGUgaG93IHRoZSBwYWNrYWdlIHdvcmtzLCB3ZSBjcmVhdGUgYSBzaW1wbGUgdG95IGRhdGFzZXQuIEl0IGNvbnNpc3RzIG9mIGEgcmVzcG9uc2UgdmFyaWFibGUgeSBhbmQgYSBwcmVkaWN0b3IgeDEuIE5vdGUgdGhhdCBpbiBwcmFjdGljZSwgd2XigJlkIHN0cml2ZSBmb3IgaW5jbHVkaW5nIG1hbnkgbW9yZSBwcmVkaWN0b3IgdmFyaWFibGVzIGFuZCBsZXQgdGhlIG1vZGVsIGNob29zZSBhbiBhcHByb3ByaWF0ZSBzdWJzZXQuIFRoZSBleGFtcGxlIGRhdGEgaGFzIDEwMCBvYnNlcnZhdGlvbnMuIFdlIGNyZWF0ZSBhbiBpbnRlcnZlbnRpb24gZWZmZWN0IGJ5IGxpZnRpbmcgdGhlIHJlc3BvbnNlIHZhcmlhYmxlIGJ5IDEwIHVuaXRzIGFmdGVyIHRpbWVwb2ludCA3MS4KYGBge3J9CnNldC5zZWVkKDEpCngxIDwtIDEwMCArIGFyaW1hLnNpbShtb2RlbCA9IGxpc3QoYXIgPSAwLjk5OSksIG4gPSAxMDApCnkgPC0gMS4yICogeDEgKyBybm9ybSgxMDApCnlbNzE6MTAwXSA8LSB5WzcxOjEwMF0gKyAxMApkYXRhIDwtIGNiaW5kKHksIHgxKQpgYGAKV2Ugbm93IGhhdmUgYSBzaW1wbGUgbWF0cml4IHdpdGggMTAwIHJvd3MgYW5kIHR3byBjb2x1bW5zOgpgYGB7cn0KZGltKGRhdGEpCmBgYAojIyBbMV0gMTAwICAgMgpgYGB7cn0KaGVhZChkYXRhKQpgYGAKCldlIGNhbiB2aXN1YWxpemUgdGhlIGdlbmVyYXRlZCBkYXRhIHVzaW5nOgpgYGB7cn0KbWF0cGxvdChkYXRhLCB0eXBlID0gImwiKQpgYGAKCiMjIFJ1bm5pbmcgYW4gYW5hbHlzaXMKClRvIGVzdGltYXRlIGEgY2F1c2FsIGVmZmVjdCwgd2UgYmVnaW4gYnkgc3BlY2lmeWluZyB3aGljaCBwZXJpb2QgaW4gdGhlIGRhdGEgc2hvdWxkIGJlIHVzZWQgZm9yIHRyYWluaW5nIHRoZSBtb2RlbCAocHJlLWludGVydmVudGlvbiBwZXJpb2QpIGFuZCB3aGljaCBwZXJpb2QgZm9yIGNvbXB1dGluZyBhIGNvdW50ZXJmYWN0dWFsIHByZWRpY3Rpb24gKHBvc3QtaW50ZXJ2ZW50aW9uIHBlcmlvZCkuCmBgYHtyfQpwcmUucGVyaW9kIDwtIGMoMSwgNzApCnBvc3QucGVyaW9kIDwtIGMoNzEsIDEwMCkKYGBgClRoaXMgc2F5cyB0aGF0IHRpbWUgcG9pbnRzIDEg4oCmIDcwIHdpbGwgYmUgdXNlZCBmb3IgdHJhaW5pbmcsIGFuZCB0aW1lIHBvaW50cyA3MSDigKYgMTAwIHdpbGwgYmUgdXNlZCBmb3IgY29tcHV0aW5nIHByZWRpY3Rpb25zLiBBbHRlcm5hdGl2ZWx5LCB3ZSBjb3VsZCBzcGVjaWZ5IHRoZSBwZXJpb2RzIGluIHRlcm1zIG9mIGRhdGVzIG9yIHRpbWUgcG9pbnRzOyBzZWUgU2VjdGlvbiA1IGZvciBhbiBleGFtcGxlLgoKVG8gcGVyZm9ybSBpbmZlcmVuY2UsIHdlIHJ1biB0aGUgYW5hbHlzaXMgdXNpbmc6CmBgYHtyfQppbXBhY3QgPC0gQ2F1c2FsSW1wYWN0KGRhdGEsIHByZS5wZXJpb2QsIHBvc3QucGVyaW9kKQpgYGAKVGhpcyBpbnN0cnVjdHMgdGhlIHBhY2thZ2UgdG8gYXNzZW1ibGUgYSBzdHJ1Y3R1cmFsIHRpbWUtc2VyaWVzIG1vZGVsLCBwZXJmb3JtIHBvc3RlcmlvciBpbmZlcmVuY2UsIGFuZCBjb21wdXRlIGVzdGltYXRlcyBvZiB0aGUgY2F1c2FsIGVmZmVjdC4gVGhlIHJldHVybiB2YWx1ZSBpcyBhIENhdXNhbEltcGFjdCBvYmplY3QuCgojIyBQbG90dGluZyB0aGUgcmVzdWx0cwoKVGhlIGVhc2llc3Qgd2F5IG9mIHZpc3VhbGl6aW5nIHRoZSByZXN1bHRzIGlzIHRvIHVzZSB0aGUgcGxvdCgpIGZ1bmN0aW9uIHRoYXQgaXMgcGFydCBvZiB0aGUgcGFja2FnZToKCmBgYHtyfQpwbG90KGltcGFjdCkKYGBgCgpCeSBkZWZhdWx0LCB0aGUgcGxvdCBjb250YWlucyB0aHJlZSBwYW5lbHMuIFRoZSBmaXJzdCBwYW5lbCBzaG93cyB0aGUgZGF0YSBhbmQgYSBjb3VudGVyZmFjdHVhbCBwcmVkaWN0aW9uIGZvciB0aGUgcG9zdC10cmVhdG1lbnQgcGVyaW9kLiBUaGUgc2Vjb25kIHBhbmVsIHNob3dzIHRoZSBkaWZmZXJlbmNlIGJldHdlZW4gb2JzZXJ2ZWQgZGF0YSBhbmQgY291bnRlcmZhY3R1YWwgcHJlZGljdGlvbnMuIFRoaXMgaXMgdGhlIHBvaW50d2lzZSBjYXVzYWwgZWZmZWN0LCBhcyBlc3RpbWF0ZWQgYnkgdGhlIG1vZGVsLiBUaGUgdGhpcmQgcGFuZWwgYWRkcyB1cCB0aGUgcG9pbnR3aXNlIGNvbnRyaWJ1dGlvbnMgZnJvbSB0aGUgc2Vjb25kIHBhbmVsLCByZXN1bHRpbmcgaW4gYSBwbG90IG9mIHRoZSBjdW11bGF0aXZlIGVmZmVjdCBvZiB0aGUgaW50ZXJ2ZW50aW9uLgoKUmVtZW1iZXIsIG9uY2UgYWdhaW4sIHRoYXQgYWxsIG9mIHRoZSBhYm92ZSBpbmZlcmVuY2VzIGRlcGVuZCBjcml0aWNhbGx5IG9uIHRoZSBhc3N1bXB0aW9uIHRoYXQgdGhlIGNvdmFyaWF0ZXMgd2VyZSBub3QgdGhlbXNlbHZlcyBhZmZlY3RlZCBieSB0aGUgaW50ZXJ2ZW50aW9uLiBUaGUgbW9kZWwgYWxzbyBhc3N1bWVzIHRoYXQgdGhlIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIGNvdmFyaWF0ZXMgYW5kIHRyZWF0ZWQgdGltZSBzZXJpZXMsIGFzIGVzdGFibGlzaGVkIGR1cmluZyB0aGUgcHJlLXBlcmlvZCwgcmVtYWlucyBzdGFibGUgdGhyb3VnaG91dCB0aGUgcG9zdC1wZXJpb2QuCgo1LiBXb3JraW5nIHdpdGggZGF0ZXMgYW5kIHRpbWVzCgpJdCBpcyBvZnRlbiBtb3JlIG5hdHVyYWwgdG8gZmVlZCBhIHRpbWUtc2VyaWVzIG9iamVjdCBpbnRvIENhdXNhbEltcGFjdCgpIHJhdGhlciB0aGFuIGEgZGF0YSBmcmFtZS4gRm9yIGV4YW1wbGUsIHdlIG1pZ2h0IGNyZWF0ZSBhIGRhdGEgdmFyaWFibGUgYXMgZm9sbG93czoKYGBge3J9CnRpbWUucG9pbnRzIDwtIHNlcS5EYXRlKGFzLkRhdGUoIjIwMTQtMDEtMDEiKSwgYnkgPSAxLCBsZW5ndGgub3V0ID0gMTAwKQpkYXRhIDwtIHpvbyhjYmluZCh5LCB4MSksIHRpbWUucG9pbnRzKQpoZWFkKGRhdGEpCmBgYAoKCldlIGNhbiBub3cgc3BlY2lmeSB0aGUgcHJlLXBlcmlvZCBhbmQgdGhlIHBvc3QtcGVyaW9kIGluIHRlcm1zIG9mIHRpbWUgcG9pbnRzIHJhdGhlciB0aGFuIGluZGljZXM6CmBgYHtyfQpwcmUucGVyaW9kIDwtIGFzLkRhdGUoYygiMjAxNC0wMS0wMSIsICIyMDE0LTAzLTExIikpCnBvc3QucGVyaW9kIDwtIGFzLkRhdGUoYygiMjAxNC0wMy0xMiIsICIyMDE0LTA0LTEwIikpCmBgYApBcyBhIHJlc3VsdCwgdGhlIHgtYXhpcyBvZiB0aGUgcGxvdCBzaG93cyB0aW1lIHBvaW50cyBpbnN0ZWFkIG9mIGluZGljZXM6CmBgYHtyfQppbXBhY3QgPC0gQ2F1c2FsSW1wYWN0KGRhdGEsIHByZS5wZXJpb2QsIHBvc3QucGVyaW9kKQpwbG90KGltcGFjdCkKYGBgCgojIyBQcmludGluZyBhIHN1bW1hcnkgdGFibGUKClRvIG9idGFpbiBhIG51bWVyaWNhbCBzdW1tYXJ5IG9mIHRoZSBhbmFseXNpcywgd2UgdXNlOiAgCgpgYGB7cn0Kc3VtbWFyeShpbXBhY3QpCmBgYAoKClRoZSAqKkF2ZXJhZ2UqKiBjb2x1bW4gdGFsa3MgYWJvdXQgdGhlIGF2ZXJhZ2UgKGFjcm9zcyB0aW1lKSBkdXJpbmcgdGhlIHBvc3QtaW50ZXJ2ZW50aW9uIHBlcmlvZCAoaW4gdGhlIGV4YW1wbGU6IHRpbWUgcG9pbnRzIDcxIHRocm91Z2ggMTAwKS4gVGhlICoqQ3VtdWxhdGl2ZSoqIGNvbHVtbiBzdW1zIHVwIGluZGl2aWR1YWwgdGltZSBwb2ludHMsIHdoaWNoIGlzIGEgdXNlZnVsIHBlcnNwZWN0aXZlIGlmIHRoZSByZXNwb25zZSB2YXJpYWJsZSByZXByZXNlbnRzIGEgZmxvdyBxdWFudGl0eSAoc3VjaCBhcyBxdWVyaWVzLCBjbGlja3MsIHZpc2l0cywgaW5zdGFsbHMsIHNhbGVzLCBvciByZXZlbnVlKSByYXRoZXIgdGhhbiBhIHN0b2NrIHF1YW50aXR5IChzdWNoIGFzIG51bWJlciBvZiB1c2VycyBvciBzdG9jayBwcmljZSkuCgpJbiB0aGUgZXhhbXBsZSwgdGhlIGVzdGltYXRlZCBhdmVyYWdlIGNhdXNhbCBlZmZlY3Qgb2YgdHJlYXRtZW50IHdhcyAxMSAocm91bmRlZCB0byBhIHdob2xlIG51bWJlcjsgZm9yIGZ1bGwgcHJlY2lzaW9uIHNlZSBgaW1wYWN0JHN1bW1hcnlgKS4gVGhpcyBpcyBiZWNhdXNlIHdlIG9ic2VydmVkIGFuIGF2ZXJhZ2UgdmFsdWUgb2YgOTkgYnV0IHdvdWxkIGhhdmUgZXhwZWN0ZWQgYW4gYXZlcmFnZSB2YWx1ZSBvZiBvbmx5IDg5LiBUaGUgOTUlIHBvc3RlcmlvciBpbnRlcnZhbCBvZiB0aGUgYXZlcmFnZSBlZmZlY3QgaXMgWzkuOCwgMTFdLiBTaW5jZSB0aGlzIGV4Y2x1ZGVzIDAsIHdlIChjb3JyZWN0bHkpIGNvbmNsdWRlIHRoYXQgdGhlIGludGVydmVudGlvbiBoYWQgYSBjYXVzYWwgZWZmZWN0IG9uIHRoZSByZXNwb25zZSB2YXJpYWJsZS4gU2luY2Ugd2UgZ2VuZXJhdGVkIHRoZSBkYXRhIG91cnNlbHZlcywgd2Uga25vdyB0aGF0IHdlIGluamVjdGVkIGEgdHJ1ZSBlZmZlY3Qgb2YgMTAsIGFuZCBzbyB0aGUgbW9kZWwgYWNjdXJhdGVseSByZWNvdmVyZWQgZ3JvdW5kIHRydXRoLiBPbmUgcmVhc29uIGZvciB0aGlzIGlzIHRoYXQgd2UgZW5zdXJlZCwgYnkgZGVzaWduLCB0aGF0IHRoZSBjb3ZhcmlhdGUgeDEgd2FzIG5vdCBpdHNlbGYgYWZmZWN0ZWQgYnkgdGhlIGludGVydmVudGlvbi4gSW4gcHJhY3RpY2UsIHdlIG11c3QgYWx3YXlzIHJlYXNvbiB3aGV0aGVyIHRoaXMgYXNzdW1wdGlvbiBpcyBqdXN0aWZpZWQuCgpGb3IgYWRkaXRpb25hbCBndWlkYW5jZSBhYm91dCB0aGUgY29ycmVjdCBpbnRlcnByZXRhdGlvbiBvZiB0aGUgc3VtbWFyeSB0YWJsZSwgdGhlIHBhY2thZ2UgcHJvdmlkZXMgYSB2ZXJiYWwgaW50ZXJwcmV0YXRpb24sIHdoaWNoIHdlIGNhbiBwcmludCB1c2luZzoKYGBge3J9CnN1bW1hcnkoaW1wYWN0LCAicmVwb3J0IikKYGBgICAKClRoZSBpbmRpdmlkdWFsIG51bWJlcnMgaW4gdGhlIHRhYmxlLCBhdCBmdWxsIHByZWNpc2lvbiwgY2FuIGJlIGFjY2Vzc2VkIHVzaW5nOiAgCmBgYHtyfQppbXBhY3Qkc3VtbWFyeQpgYGAgIAoKU2VlIGJlbG93IGZvciB0aXBzIG9uIGhvdyB0byB1c2UgdGhlc2UgY29tbWFuZHMgd2l0aCBrbml0ciAvIFIgTWFya2Rvd24uCgojIyBBZGp1c3RpbmcgdGhlIG1vZGVsCgpTbyBmYXIsIHdl4oCZdmUgc2ltcGx5IGxldCB0aGUgcGFja2FnZSBkZWNpZGUgaG93IHRvIGNvbnN0cnVjdCBhIHRpbWUtc2VyaWVzIG1vZGVsIGZvciB0aGUgYXZhaWxhYmxlIGRhdGEuIEhvd2V2ZXIsIHRoZXJlIGFyZSBzZXZlcmFsIG9wdGlvbnMgdGhhdCBhbGxvdyB1cyB0byBnYWluIGEgbGl0dGxlIG1vcmUgY29udHJvbCBvdmVyIHRoaXMgcHJvY2Vzcy4gVGhlc2Ugb3B0aW9ucyBhcmUgcGFzc2VkIGludG8gYG1vZGVsLmFyZ3NgIGFzIGluZGl2aWR1YWwgbGlzdCBlbGVtZW50cywgZm9yIGV4YW1wbGU6CgpgYGB7ciwgZXZhbD1GQUxTRX0KaW1wYWN0IDwtIENhdXNhbEltcGFjdCguLi4sIG1vZGVsLmFyZ3MgPSBsaXN0KG5pdGVyID0gNTAwMCwgbnNlYXNvbnMgPSA3KSkKYGBgCgojIyMgQXZhaWxhYmxlIG9wdGlvbnMgIAoKLSBgbml0ZXJgIE51bWJlciBvZiBNQ01DIHNhbXBsZXMgdG8gZHJhdy4gTW9yZSBzYW1wbGVzIGxlYWQgdG8gbW9yZSBhY2N1cmF0ZSBpbmZlcmVuY2VzLiBEZWZhdWx0cyB0byAqKjEwMDAqKi4gIAotIGBzdGFuZGFyZGl6ZS5kYXRhYCBXaGV0aGVyIHRvIHN0YW5kYXJkaXplIGFsbCBjb2x1bW5zIG9mIHRoZSBkYXRhIGJlZm9yZSBmaXR0aW5nIHRoZSBtb2RlbC4gVGhpcyBpcyBlcXVpdmFsZW50IHRvIGFuIGVtcGlyaWNhbCBCYXllcyBhcHByb2FjaCB0byBzZXR0aW5nIHRoZSBwcmlvcnMuIEl0IGVuc3VyZXMgdGhhdCByZXN1bHRzIGFyZSBpbnZhcmlhbnQgdG8gbGluZWFyIHRyYW5zZm9ybWF0aW9ucyBvZiB0aGUgZGF0YS4gRGVmYXVsdHMgdG8gKipUUlVFKiouICAKLSBgcHJpb3IubGV2ZWwuc2RgIFByaW9yIHN0YW5kYXJkIGRldmlhdGlvbiBvZiB0aGUgR2F1c3NpYW4gcmFuZG9tIHdhbGsgb2YgdGhlIGxvY2FsIGxldmVsLiBFeHByZXNzZWQgaW4gdGVybXMgb2YgZGF0YSBzdGFuZGFyZCBkZXZpYXRpb25zLiBEZWZhdWx0cyB0byAqKjAuMDEqKiwgYSB0eXBpY2FsIGNob2ljZSBmb3Igd2VsbC1iZWhhdmVkIGFuZCBzdGFibGUgZGF0YXNldHMgd2l0aCBsb3cgcmVzaWR1YWwgdm9sYXRpbGl0eSBhZnRlciByZWdyZXNzaW5nIG91dCBrbm93biBwcmVkaWN0b3JzIChlLmcuLCB3ZWIgc2VhcmNoZXMgb3Igc2FsZXMgaW4gaGlnaCBxdWFudGl0aWVzKS4gV2hlbiBpbiBkb3VidCwgYSBzYWZlciBvcHRpb24gaXMgdG8gdXNlICoqMC4xKiosIGFzIHZhbGlkYXRlZCBvbiBzeW50aGV0aWMgZGF0YSwgYWx0aG91Z2ggdGhpcyBtYXkgc29tZXRpbWVzIGdpdmUgcmlzZSB0byB1bnJlYWxpc3RpY2FsbHkgd2lkZSBwcmVkaWN0aW9uIGludGVydmFscy4gIAotIGBuc2Vhc29uc2AgUGVyaW9kIG9mIHRoZSBzZWFzb25hbCBjb21wb25lbnRzLiBJbiBvcmRlciB0byBpbmNsdWRlIGEgc2Vhc29uYWwgY29tcG9uZW50LCBzZXQgdGhpcyB0byBhIHdob2xlIG51bWJlciBncmVhdGVyIHRoYW4gMS4gRm9yIGV4YW1wbGUsIGlmIHRoZSBkYXRhIHJlcHJlc2VudCBkYWlseSBvYnNlcnZhdGlvbnMsIHVzZSA3IGZvciBhIGRheS1vZi13ZWVrIGNvbXBvbmVudC4gVGhpcyBpbnRlcmZhY2UgY3VycmVudGx5IG9ubHkgc3VwcG9ydHMgdXAgdG8gb25lIHNlYXNvbmFsIGNvbXBvbmVudC4gVG8gc3BlY2lmeSBtdWx0aXBsZSBzZWFzb25hbCBjb21wb25lbnRzLCB1c2UgYnN0cyB0byBzcGVjaWZ5IHRoZSBtb2RlbCBkaXJlY3RseSwgdGhlbiBwYXNzIHRoZSBmaXR0ZWQgbW9kZWwgaW4gYXMgYnN0cy5tb2RlbC4gRGVmYXVsdHMgdG8gKioxKiosIHdoaWNoIG1lYW5zIG5vIHNlYXNvbmFsIGNvbXBvbmVudCBpcyB1c2VkLiAgCi0gYHNlYXNvbi5kdXJhdGlvbmAgRHVyYXRpb24gb2YgZWFjaCBzZWFzb24sIGkuZS4sIG51bWJlciBvZiBkYXRhIHBvaW50cyBlYWNoICBzZWFzb24gc3BhbnMuIERlZmF1bHRzIHRvICoqMSoqLiBGb3IgZXhhbXBsZSwgdG8gYWRkIGEgZGF5LW9mLXdlZWsgY29tcG9uZW50IHRvIGRhdGEgd2l0aCBkYWlseSBncmFudWxhcml0eSwgKip1c2UgbW9kZWwuYXJncyA9IGxpc3QobnNlYXNvbnMgPSA3LCBzZWFzb24uZHVyYXRpb24gPSAxKSoqLiBUbyBhZGQgYSBkYXktb2Ytd2VlayBjb21wb25lbnQgdG8gZGF0YSB3aXRoIGhvdXJseSBncmFudWxhcml0eSwgc2V0ICoqbW9kZWwuYXJncyA9IGxpc3QobnNlYXNvbnMgPSA3LCBzZWFzb24uZHVyYXRpb24gPSAyNCkqKi4gIAotICBgZHluYW1pYy5yZWdyZXNzaW9uYCBXaGV0aGVyIHRvIGluY2x1ZGUgdGltZS12YXJ5aW5nIHJlZ3Jlc3Npb24gY29lZmZpY2llbnRzLiBJbiBjb21iaW5hdGlvbiB3aXRoIGEgdGltZS12YXJ5aW5nIGxvY2FsIHRyZW5kIG9yIGV2ZW4gYSB0aW1lLXZhcnlpbmcgbG9jYWwgbGV2ZWwsIHRoaXMgb2Z0ZW4gbGVhZHMgdG8gb3ZlcnNwZWNpZmljYXRpb24sIGluIHdoaWNoIGNhc2UgYSBzdGF0aWMgcmVncmVzc2lvbiBpcyBzYWZlci4gRGVmYXVsdHMgdG8gKipGQUxTRSoqLiAgCgojI1VzaW5nIGEgY3VzdG9tIG1vZGVsICAgIAoKSW5zdGVhZCBvZiB1c2luZyB0aGUgZGVmYXVsdCBtb2RlbCBjb25zdHJ1Y3RlZCBieSB0aGUgQ2F1c2FsSW1wYWN0IHBhY2thZ2UsIHdlIGNhbiB1c2UgdGhlIGJzdHMgcGFja2FnZSB0byBzcGVjaWZ5IG91ciBvd24gbW9kZWwuIFRoaXMgcHJvdmlkZXMgdGhlIGdyZWF0ZXN0IGRlZ3JlZSBvZiBmbGV4aWJpbGl0eS4gIAoKQmVmb3JlIGNvbnN0cnVjdGluZyBhIGN1c3RvbSBtb2RlbCwgd2Ugc2V0IHRoZSBvYnNlcnZlZCBkYXRhIGluIHRoZSBwb3N0LXRyZWF0bWVudCBwZXJpb2QgdG8gTkEsIHJlZmxlY3RpbmcgdGhlIGZhY3QgdGhhdCB0aGUgY291bnRlcmZhY3R1YWwgcmVzcG9uc2UgaXMgdW5vYnNlcnZlZCBhZnRlciB0aGUgaW50ZXJ2ZW50aW9uLiBXZSBrZWVwIGEgY29weSBvZiB0aGUgYWN0dWFsIG9ic2VydmVkIHJlc3BvbnNlIGluIHRoZSB2YXJpYWJsZSBgcG9zdC5wZXJpb2QucmVzcG9uc2VgLiAgCgpgYGB7cn0KcG9zdC5wZXJpb2QgPC0gYyg3MSwgMTAwKQpwb3N0LnBlcmlvZC5yZXNwb25zZSA8LSB5W3Bvc3QucGVyaW9kWzFdIDogcG9zdC5wZXJpb2RbMl1dCnlbcG9zdC5wZXJpb2RbMV0gOiBwb3N0LnBlcmlvZFsyXV0gPC0gTkEKYGBgCgpXZSBuZXh0IHNldCB1cCBhbmQgZXN0aW1hdGUgYSB0aW1lLXNlcmllcyBtb2RlbCB1c2luZyB0aGUgYnN0cyBwYWNrYWdlLiBIZXJlIGlzIGEgc2ltcGxlIGV4YW1wbGU6ICAKCmBgYHtyfQpzcyA8LSBBZGRMb2NhbExldmVsKGxpc3QoKSwgeSkKYnN0cy5tb2RlbCA8LSBic3RzKHkgfiB4MSwgc3MsIG5pdGVyID0gMTAwMCkKYGBgCgpGaW5hbGx5LCB3ZSBjYWxsIGBDYXVzYWxJbXBhY3RgIEluc3RlYWQgb2YgcHJvdmlkaW5nIGlucHV0IGRhdGEsIHdlIHNpbXBseSBwYXNzIGluIHRoZSBmaXR0ZWQgbW9kZWwgb2JqZWN0IChgYnN0cy5tb2RlbGApLiBXZSBhbHNvIG5lZWQgdG8gcHJvdmlkZSB0aGUgYWN0dWFsIG9ic2VydmVkIHJlc3BvbnNlLiBUaGlzIGlzIG5lZWRlZCBzbyB0aGF0IHRoZSBwYWNrYWdlIGNhbiBjb21wdXRlIHRoZSBkaWZmZXJlbmNlIGJldHdlZW4gcHJlZGljdGVkIHJlc3BvbnNlIChzdG9yZWQgaW4gYGJzdHMubW9kZWxgKSBhbmQgYWN0dWFsIG9ic2VydmVkIHJlc3BvbnNlIChzdG9yZWQgaW4gYHBvc3QucGVyaW9kLnJlc3BvbnNlYCkuICAKCmBgYHtyfQppbXBhY3QgPC0gQ2F1c2FsSW1wYWN0KGJzdHMubW9kZWwgPSBic3RzLm1vZGVsLAogICAgICAgICAgICAgICAgICAgICAgIHBvc3QucGVyaW9kLnJlc3BvbnNlID0gcG9zdC5wZXJpb2QucmVzcG9uc2UpCmBgYAoKVGhlIHJlc3VsdHMgY2FuIGJlIGluc3BlY3RlZCBpbiB0aGUgdXN1YWwgd2F5OiAgCgpgYGB7cn0KcGxvdChpbXBhY3QpCnN1bW1hcnkoaW1wYWN0KQpzdW1tYXJ5KGltcGFjdCwgInJlcG9ydCIpCmBgYA==