Intro

This R package implements an approach to estimating the causal effect of a designed intervention on a time series. For example, how many additional daily clicks were generated by an advertising campaign? Answering a question like this can be difficult when a randomized experiment is not available.

Given a response time series (e.g., clicks) and a set of control time series (e.g., clicks in non-affected markets or clicks on other sites), the package constructs a Bayesian structural time-series model. This model is then used to try and predict the counterfactual, i.e., how the response metric would have evolved after the intervention if the intervention had never occurred. For a quick overview, watch the tutorial video. For details, see: Brodersen et al., Annals of Applied Statistics (2015).

We will apply LIWC only in certain categories:

##  [1] "i"            "we"           "you"          "shehe"        "they"        
##  [6] "ipron"        "negate"       "compare"      "posemo"       "negemo"      
## [11] "anx"          "anger"        "sad"          "social"       "family"      
## [16] "friend"       "female"       "male"         "insight"      "cause"       
## [21] "discrep"      "tentat"       "certain"      "differ"       "see"         
## [26] "hear"         "feel"         "body"         "health"       "sexual"      
## [31] "ingest"       "affiliation"  "achiev"       "power"        "reward"      
## [36] "risk"         "focuspast"    "focuspresent" "focusfuture"  "relativ"     
## [41] "work"         "leisure"      "home"         "money"        "relig"       
## [46] "death"        "informal"     "swear"        "assent"       "nonflu"      
## [51] "filler"

LIWC

Data Example

Example: In our example data we consider two different days (-1,1), two different users (A, B). User A tweeted 3 times during day -1, and 2 times during day 1. A mentioned happy words 2 times in the first tweet and 1 time in the 3th tweet. During day 1 both users tweeted 2 times. A and B mentioned happyness 1 time. And A mentioned 1 time ipron.

days_tweet <- c(rep(-1, 4), rep(1,4))
id <- c(rep("A", 3), "B", rep("A",2), rep("B", 2))
id_tweet <- sample(30000:40000, 8, replace = F)
happy <- c(2, 0, 1, 0, 1, 0, 1, 0)
ipron <- c(0, 0, 0, 1, 1, 0, 0, 0)

d <- tibble(days_tweet, id, id_tweet, happy, ipron) 

d %>% 
  kable() %>% 
  kable_styling(bootstrap_options = c("striped", "hover", "condensed"),
                full_width = FALSE)
days_tweet id id_tweet happy ipron
-1 A 31949 2 0
-1 A 36706 0 0
-1 A 38442 1 0
-1 B 34539 0 1
1 A 32656 1 1
1 A 30282 0 0
1 B 37801 1 0
1 B 36020 0 0

First Approach

Steps:

  1. We compute the mean of word usage by day.
d %>% 
  group_by(days_tweet) %>% 
  summarise_at(vars(happy:ipron), mean) %>% 
  ungroup() %>% 
  kable() %>% 
  kable_styling(bootstrap_options = c("striped", "hover", "condensed"),
                full_width = FALSE)
days_tweet happy ipron
-1 0.75 0.25
1 0.50 0.25

Second Approach

Steps:

  1. We first computed the mean value per day and user.

  2. Then we computed the mean value per day.

d %>% 
  group_by(days_tweet, id) %>% 
  summarise_at(vars(happy:ipron), mean) %>% 
  ungroup() %>% 
  group_by(days_tweet) %>% 
  summarise_at(vars(happy:ipron), mean) %>% 
  ungroup() %>% 
  kable() %>% 
  kable_styling(bootstrap_options = c("striped", "hover", "condensed"),
                full_width = FALSE)
days_tweet happy ipron
-1 0.5 0.50
1 0.5 0.25

This is values in NEDA and baseline with the first approach:

baseline_liwc %>% 
  select(-text, -created_at, -id_tweet, -id) %>% 
  group_by(days_tweet) %>% 
  summarise_all(mean) %>% 
  ungroup() %>% 
  pivot_longer(cols = fun:filler, names_to = "categ", values_to = "values_baseline") -> ci_baseline

neda_liwc %>% 
  select(-id_tweet, -id) %>% 
  group_by(days_tweet) %>% 
  summarise_all(mean) %>% 
  ungroup() %>% 
  pivot_longer(-days_tweet, names_to = "categ", values_to = "values_neda") -> ci_neda_liwc 
  
pre_period <- c(1, 16)
post_period <- c(17, 31)

ci_baseline %>% 
  inner_join(ci_neda_liwc) -> d_first
d_first %>% 
  pivot_longer(cols = values_baseline:values_neda,
               names_to = "names", values_to = "values") %>% 
  mutate(names = case_when(names == "values_baseline" ~ "Baseline",
                           names == "values_neda" ~ "NEDA")) %>% 
  filter(categ %in% sel_cat[1:24]) %>% 
  ggplot(aes(x = days_tweet, y = values, color = names)) +
  geom_line() +
  facet_wrap(categ ~., scales = "free", ncol = 6) +
  labs(color = "", title = "First Approach") +
  theme(legend.position="top")

This is values in NEDA and baseline with the second approach:

Causal Impact Package

In the table bellow we compare result from computing Causal Impact Package using this two different approach. Only p_value < 0.05 are included. Columns contains information about:

  1. Word Category: Word category in LIWC.

  2. 1st App Relative Eff.: Cummulative relative effect in percentages with the first approach.

  3. 1st P Value: P Value first approach.

  4. 2nd App Relative Eff.: Cummulative relative effect in percentages with the second approach.

d_first %>% 
  select(categ, values_neda, values_baseline) %>% 
  nest(data = - categ) %>% 
  mutate(mod = map(data, ~CausalImpact::CausalImpact(., pre_period, post_period))) -> ci
  
ci %>% 
  mutate(summary_mod = map(mod, "summary")) %>% 
  filter(!map_lgl(summary_mod, is.null)) -> ci_resul 

ci_resul %>% 
  mutate(p = map(summary_mod, "p")) %>% 
  mutate(p = map_dbl(p, 1)) %>% 
  filter(p < 0.05) %>% 
  mutate(relative_effect = map(summary_mod, "RelEffect")) %>% 
  mutate(relative_effect = map_dbl(relative_effect, 2))-> sig_cat

sig_cat %>% 
  filter(categ %in% sel_cat) -> sig_cat

sig_cat %>% 
  arrange(desc(relative_effect)) %>% 
  select(categ, first_relative_effect = relative_effect, p_first = p) -> first_ap

d_second %>% 
  select(categ, values_neda, values_baseline) %>% 
  nest(data = - categ) %>% 
  mutate(mod = map(data, ~CausalImpact::CausalImpact(., pre_period, post_period))) -> ci
  
ci %>% 
  mutate(summary_mod = map(mod, "summary")) %>% 
  filter(!map_lgl(summary_mod, is.null)) -> ci_resul 

ci_resul %>% 
  mutate(p = map(summary_mod, "p")) %>% 
  mutate(p = map_dbl(p, 1)) %>% 
  filter(p < 0.05) %>% 
  mutate(relative_effect = map(summary_mod, "RelEffect")) %>% 
  mutate(relative_effect = map_dbl(relative_effect, 2))-> sig_cat

sig_cat %>% 
  filter(categ %in% sel_cat) -> sig_cat

sig_cat %>% 
  arrange(desc(relative_effect)) %>% 
  select(categ, second_relative_effect = relative_effect, p_second = p) -> second_ap

second_ap %>% 
  mutate(categ = str_to_title(categ)) %>% 
  mutate_at(vars(second_relative_effect), ~.*100) %>% 
  mutate_if(is.numeric, ~round(., digits = 3)) %>% 
  mutate_at(vars(second_relative_effect), function(x){
    cell_spec(x, "html", color = spec_color(x), bold = T)
  }) %>% 
  kable("html", escape = F,
        align = "lrr",
        col.names = c("Word Category", "Relative Eff.(%)",
                      "P Value")) %>% 
  kable_styling(bootstrap_options = c("striped", "hover", "condensed"),
                full_width = FALSE)
Word Category Relative Eff.(%) P Value
Female 17.374 0.001
Family 7.842 0.002
Anx 7.495 0.001
Relig 5.527 0.029
Money 5.339 0.003
They 4.088 0.010
Achiev 3.877 0.005
Negate 3.315 0.001
Health 2.737 0.005
Power 2.26 0.016
Negemo 2.135 0.041
Informal 1.17 0.032
Ipron -1.493 0.013
You -2.027 0.041
Discrep -2.234 0.030
Differ -2.6 0.010
Tentat -3.404 0.002
Posemo -3.493 0.001
Shehe -6.757 0.027
Affiliation -7.352 0.002
LS0tCnRpdGxlOiAiQ2F1c2FsIEltcGFjdCIgCmNsZWFuOiB0cnVlCm91dHB1dDoKICBib29rZG93bjo6aHRtbF9kb2N1bWVudDI6CiAgICBudW1iZXJfc2VjdGlvbnM6IGZhbHNlCiAgICBjb2RlX2Rvd25sb2FkOiB0cnVlCiAgICBjb2RlX2ZvbGRpbmc6IGhpZGUKICAgIHNlbGZfY29udGFpbmVkOiB0cnVlCiAgICB0b2M6IHRydWUKICAgIHRvY19mbG9hdDogZmFsc2UKLS0tCgpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KGVjaG8gPSBUUlVFLCB3YXJuaW5nID0gRkFMU0UsIG1lc3NhZ2UgPSBGQUxTRSkKCnhmdW46OnBrZ19hdHRhY2goInRpZHl2ZXJzZSIsICJsdWJyaWRhdGUiLCAia2FibGVFeHRyYSIpCgp0aGVtZV9zZXQodGhlbWVfbGluZWRyYXcoKSkKYGBgCgojIyBJbnRybwoKVGhpcyBSIHBhY2thZ2UgaW1wbGVtZW50cyBhbiBhcHByb2FjaCB0byBlc3RpbWF0aW5nIHRoZSBjYXVzYWwgZWZmZWN0IG9mIGEgZGVzaWduZWQgaW50ZXJ2ZW50aW9uIG9uIGEgdGltZSBzZXJpZXMuIEZvciBleGFtcGxlLCBob3cgbWFueSBhZGRpdGlvbmFsIGRhaWx5IGNsaWNrcyB3ZXJlIGdlbmVyYXRlZCBieSBhbiBhZHZlcnRpc2luZyBjYW1wYWlnbj8gQW5zd2VyaW5nIGEgcXVlc3Rpb24gbGlrZSB0aGlzIGNhbiBiZSBkaWZmaWN1bHQgd2hlbiBhIHJhbmRvbWl6ZWQgZXhwZXJpbWVudCBpcyBub3QgYXZhaWxhYmxlLgoKR2l2ZW4gYSByZXNwb25zZSB0aW1lIHNlcmllcyAoZS5nLiwgY2xpY2tzKSBhbmQgYSBzZXQgb2YgY29udHJvbCB0aW1lIHNlcmllcyAoZS5nLiwgY2xpY2tzIGluIG5vbi1hZmZlY3RlZCBtYXJrZXRzIG9yIGNsaWNrcyBvbiBvdGhlciBzaXRlcyksIHRoZSBwYWNrYWdlIGNvbnN0cnVjdHMgYSBCYXllc2lhbiBzdHJ1Y3R1cmFsIHRpbWUtc2VyaWVzIG1vZGVsLiBUaGlzIG1vZGVsIGlzIHRoZW4gdXNlZCB0byB0cnkgYW5kIHByZWRpY3QgdGhlIGNvdW50ZXJmYWN0dWFsLCBpLmUuLCBob3cgdGhlIHJlc3BvbnNlIG1ldHJpYyB3b3VsZCBoYXZlIGV2b2x2ZWQgYWZ0ZXIgdGhlIGludGVydmVudGlvbiBpZiB0aGUgaW50ZXJ2ZW50aW9uIGhhZCBuZXZlciBvY2N1cnJlZC4gRm9yIGEgcXVpY2sgb3ZlcnZpZXcsIHdhdGNoIHRoZSB0dXRvcmlhbCB2aWRlby4gRm9yIGRldGFpbHMsIHNlZTogQnJvZGVyc2VuIGV0IGFsLiwgQW5uYWxzIG9mIEFwcGxpZWQgU3RhdGlzdGljcyAoMjAxNSkuCgpXZSB3aWxsIGFwcGx5IExJV0Mgb25seSBpbiBjZXJ0YWluIGNhdGVnb3JpZXM6CgpgYGB7ciBlY2hvPUZBTFNFfQpiYXNlbGluZV9saXdjIDwtIHJlYWRfcmRzKGhlcmU6OmhlcmUoImRhdGEiLCAiYmFzZWxpbmVfbGl3Yy5yZHMiKSkKbmVkYV9saXdjIDwtIHJlYWRfcmRzKGhlcmU6OmhlcmUoImRhdGEiLCAibmVkYV9saXdjLnJkcyIpKSAlPiUgCiAgc2VsZWN0KC10ZXh0LCAtY3JlYXRlZF9hdF90d2VldCkgJT4lIAogIGZpbHRlcihhYnMoZGF5c190d2VldCkgPD0gMTUpCgpzZWxfY2F0IDwtIGMoCiAgImkiLCJ3ZSIsInlvdSIsInNoZWhlIiwidGhleSIsImlwcm9uIiwibmVnYXRlIiwiY29tcGFyZSIsInBvc2VtbyIsIm5lZ2VtbyIsImFueCIsImFuZ2VyIiwic2FkIiwic29jaWFsIiwiZmFtaWx5IiwiZnJpZW5kIiwiZmVtYWxlIiwibWFsZSIsImluc2lnaHQiLCJjYXVzZSIsImRpc2NyZXAiLCJ0ZW50YXQiLCJjZXJ0YWluIiwiZGlmZmVyIiwic2VlIiwiaGVhciIsImZlZWwiLCJib2R5IiwiaGVhbHRoIiwic2V4dWFsIiwiaW5nZXN0IiwiYWZmaWxpYXRpb24iLCJhY2hpZXYiLCJwb3dlciIsInJld2FyZCIsInJpc2siLCJmb2N1c3Bhc3QiLCJmb2N1c3ByZXNlbnQiLCJmb2N1c2Z1dHVyZSIsInJlbGF0aXYiLCJ3b3JrIiwibGVpc3VyZSIsImhvbWUiLCJtb25leSIsInJlbGlnIiwiZGVhdGgiLCJpbmZvcm1hbCIsInN3ZWFyIiwiYXNzZW50Iiwibm9uZmx1IiwiZmlsbGVyIgopCgpzZWxfY2F0CmBgYAoKIyMgTElXQwoKIyMjIERhdGEgRXhhbXBsZQoKRXhhbXBsZTogSW4gb3VyIGV4YW1wbGUgZGF0YSB3ZSBjb25zaWRlciB0d28gZGlmZmVyZW50IGRheXMgKC0xLDEpLCB0d28gZGlmZmVyZW50IHVzZXJzIChBLCBCKS4gVXNlciBgQWAgdHdlZXRlZCBgMyB0aW1lc2AgZHVyaW5nIGRheSBgLTFgLCBhbmQgYDIgdGltZXNgIGR1cmluZyBkYXkgYDFgLiBgQWAgbWVudGlvbmVkIGhhcHB5IHdvcmRzIDIgdGltZXMgaW4gdGhlIGZpcnN0IHR3ZWV0IGFuZCAxIHRpbWUgaW4gdGhlIDN0aCB0d2VldC4gRHVyaW5nIGRheSBgMWAgYm90aCB1c2VycyB0d2VldGVkIGAyYCB0aW1lcy4gQSBhbmQgQiBtZW50aW9uZWQgaGFwcHluZXNzIGAxYCB0aW1lLiBBbmQgQSBtZW50aW9uZWQgYDFgIHRpbWUgYGlwcm9uYC4gICAgCgpgYGB7cn0KZGF5c190d2VldCA8LSBjKHJlcCgtMSwgNCksIHJlcCgxLDQpKQppZCA8LSBjKHJlcCgiQSIsIDMpLCAiQiIsIHJlcCgiQSIsMiksIHJlcCgiQiIsIDIpKQppZF90d2VldCA8LSBzYW1wbGUoMzAwMDA6NDAwMDAsIDgsIHJlcGxhY2UgPSBGKQpoYXBweSA8LSBjKDIsIDAsIDEsIDAsIDEsIDAsIDEsIDApCmlwcm9uIDwtIGMoMCwgMCwgMCwgMSwgMSwgMCwgMCwgMCkKCmQgPC0gdGliYmxlKGRheXNfdHdlZXQsIGlkLCBpZF90d2VldCwgaGFwcHksIGlwcm9uKSAKCmQgJT4lIAogIGthYmxlKCkgJT4lIAogIGthYmxlX3N0eWxpbmcoYm9vdHN0cmFwX29wdGlvbnMgPSBjKCJzdHJpcGVkIiwgImhvdmVyIiwgImNvbmRlbnNlZCIpLAogICAgICAgICAgICAgICAgZnVsbF93aWR0aCA9IEZBTFNFKQpgYGAKCiMjIyMgRmlyc3QgQXBwcm9hY2gKClN0ZXBzOgoKMS4gV2UgY29tcHV0ZSB0aGUgbWVhbiBvZiB3b3JkIHVzYWdlIGJ5IGRheS4KCmBgYHtyfQpkICU+JSAKICBncm91cF9ieShkYXlzX3R3ZWV0KSAlPiUgCiAgc3VtbWFyaXNlX2F0KHZhcnMoaGFwcHk6aXByb24pLCBtZWFuKSAlPiUgCiAgdW5ncm91cCgpICU+JSAKICBrYWJsZSgpICU+JSAKICBrYWJsZV9zdHlsaW5nKGJvb3RzdHJhcF9vcHRpb25zID0gYygic3RyaXBlZCIsICJob3ZlciIsICJjb25kZW5zZWQiKSwKICAgICAgICAgICAgICAgIGZ1bGxfd2lkdGggPSBGQUxTRSkKYGBgCgojIyMjIFNlY29uZCBBcHByb2FjaAoKU3RlcHM6CgoxLiBXZSBmaXJzdCBjb21wdXRlZCB0aGUgbWVhbiB2YWx1ZSBwZXIgZGF5IGFuZCB1c2VyLgoKMS4gVGhlbiB3ZSBjb21wdXRlZCB0aGUgbWVhbiB2YWx1ZSBwZXIgZGF5LgoKYGBge3J9CmQgJT4lIAogIGdyb3VwX2J5KGRheXNfdHdlZXQsIGlkKSAlPiUgCiAgc3VtbWFyaXNlX2F0KHZhcnMoaGFwcHk6aXByb24pLCBtZWFuKSAlPiUgCiAgdW5ncm91cCgpICU+JSAKICBncm91cF9ieShkYXlzX3R3ZWV0KSAlPiUgCiAgc3VtbWFyaXNlX2F0KHZhcnMoaGFwcHk6aXByb24pLCBtZWFuKSAlPiUgCiAgdW5ncm91cCgpICU+JSAKICBrYWJsZSgpICU+JSAKICBrYWJsZV9zdHlsaW5nKGJvb3RzdHJhcF9vcHRpb25zID0gYygic3RyaXBlZCIsICJob3ZlciIsICJjb25kZW5zZWQiKSwKICAgICAgICAgICAgICAgIGZ1bGxfd2lkdGggPSBGQUxTRSkKYGBgCgpUaGlzIGlzIHZhbHVlcyBpbiBORURBIGFuZCBiYXNlbGluZSB3aXRoIHRoZSBfX2ZpcnN0IGFwcHJvYWNoX186CmBgYHtyIH0KYmFzZWxpbmVfbGl3YyAlPiUgCiAgc2VsZWN0KC10ZXh0LCAtY3JlYXRlZF9hdCwgLWlkX3R3ZWV0LCAtaWQpICU+JSAKICBncm91cF9ieShkYXlzX3R3ZWV0KSAlPiUgCiAgc3VtbWFyaXNlX2FsbChtZWFuKSAlPiUgCiAgdW5ncm91cCgpICU+JSAKICBwaXZvdF9sb25nZXIoY29scyA9IGZ1bjpmaWxsZXIsIG5hbWVzX3RvID0gImNhdGVnIiwgdmFsdWVzX3RvID0gInZhbHVlc19iYXNlbGluZSIpIC0+IGNpX2Jhc2VsaW5lCgpuZWRhX2xpd2MgJT4lIAogIHNlbGVjdCgtaWRfdHdlZXQsIC1pZCkgJT4lIAogIGdyb3VwX2J5KGRheXNfdHdlZXQpICU+JSAKICBzdW1tYXJpc2VfYWxsKG1lYW4pICU+JSAKICB1bmdyb3VwKCkgJT4lIAogIHBpdm90X2xvbmdlcigtZGF5c190d2VldCwgbmFtZXNfdG8gPSAiY2F0ZWciLCB2YWx1ZXNfdG8gPSAidmFsdWVzX25lZGEiKSAtPiBjaV9uZWRhX2xpd2MgCiAgCnByZV9wZXJpb2QgPC0gYygxLCAxNikKcG9zdF9wZXJpb2QgPC0gYygxNywgMzEpCgpjaV9iYXNlbGluZSAlPiUgCiAgaW5uZXJfam9pbihjaV9uZWRhX2xpd2MpIC0+IGRfZmlyc3QKYGBgCgpgYGB7ciBmaWcuaGVpZ2h0PSAxMH0KZF9maXJzdCAlPiUgCiAgcGl2b3RfbG9uZ2VyKGNvbHMgPSB2YWx1ZXNfYmFzZWxpbmU6dmFsdWVzX25lZGEsCiAgICAgICAgICAgICAgIG5hbWVzX3RvID0gIm5hbWVzIiwgdmFsdWVzX3RvID0gInZhbHVlcyIpICU+JSAKICBtdXRhdGUobmFtZXMgPSBjYXNlX3doZW4obmFtZXMgPT0gInZhbHVlc19iYXNlbGluZSIgfiAiQmFzZWxpbmUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICBuYW1lcyA9PSAidmFsdWVzX25lZGEiIH4gIk5FREEiKSkgJT4lIAogIGZpbHRlcihjYXRlZyAlaW4lIHNlbF9jYXRbMToyNF0pICU+JSAKICBnZ3Bsb3QoYWVzKHggPSBkYXlzX3R3ZWV0LCB5ID0gdmFsdWVzLCBjb2xvciA9IG5hbWVzKSkgKwogIGdlb21fbGluZSgpICsKICBmYWNldF93cmFwKGNhdGVnIH4uLCBzY2FsZXMgPSAiZnJlZSIsIG5jb2wgPSA2KSArCiAgbGFicyhjb2xvciA9ICIiLCB0aXRsZSA9ICJGaXJzdCBBcHByb2FjaCIpICsKICB0aGVtZShsZWdlbmQucG9zaXRpb249InRvcCIpCmBgYAoKVGhpcyBpcyB2YWx1ZXMgaW4gTkVEQSBhbmQgYmFzZWxpbmUgd2l0aCB0aGUgX19zZWNvbmQgYXBwcm9hY2hfXzoKYGBge3IgZWNobz1GQUxTRX0KYmFzZWxpbmVfbGl3YyAlPiUgCiAgc2VsZWN0KC10ZXh0LCAtY3JlYXRlZF9hdCwgLWlkX3R3ZWV0KSAlPiUgCiAgZ3JvdXBfYnkoZGF5c190d2VldCwgaWQpICU+JSAKICBzdW1tYXJpc2VfYWxsKG1lYW4pICU+JSAKICB1bmdyb3VwKCkgJT4lIAogIHNlbGVjdCgtaWQpICU+JSAKICBncm91cF9ieShkYXlzX3R3ZWV0KSAlPiUgCiAgc3VtbWFyaXNlX2FsbChtZWFuKSAlPiUgCiAgdW5ncm91cCgpICU+JSAKICBwaXZvdF9sb25nZXIoY29scyA9IGZ1bjpmaWxsZXIsIG5hbWVzX3RvID0gImNhdGVnIiwgdmFsdWVzX3RvID0gInZhbHVlc19iYXNlbGluZSIpIC0+IGNpX2Jhc2VsaW5lCgpuZWRhX2xpd2MgJT4lIAogIHNlbGVjdCgtaWRfdHdlZXQpICU+JSAKICBncm91cF9ieShkYXlzX3R3ZWV0LCBpZCkgJT4lIAogIHN1bW1hcmlzZV9hbGwobWVhbikgJT4lIAogIHVuZ3JvdXAoKSAlPiUgCiAgc2VsZWN0KC1pZCkgJT4lIAogIGdyb3VwX2J5KGRheXNfdHdlZXQpICU+JSAKICBzdW1tYXJpc2VfYWxsKG1lYW4pICU+JSAKICB1bmdyb3VwKCkgJT4lIAogIHBpdm90X2xvbmdlcigtZGF5c190d2VldCwgbmFtZXNfdG8gPSAiY2F0ZWciLCB2YWx1ZXNfdG8gPSAidmFsdWVzX25lZGEiKSAtPiBjaV9uZWRhX2xpd2MgCiAgCnByZV9wZXJpb2QgPC0gYygxLCAxNikKcG9zdF9wZXJpb2QgPC0gYygxNywgMzEpCgpjaV9iYXNlbGluZSAlPiUgCiAgaW5uZXJfam9pbihjaV9uZWRhX2xpd2MpIC0+IGRfc2Vjb25kCmBgYAoKYGBge3IgZWNobz1GQUxTRSwgZmlnLmhlaWdodD0xMH0KZF9zZWNvbmQgJT4lIAogIHBpdm90X2xvbmdlcihjb2xzID0gdmFsdWVzX2Jhc2VsaW5lOnZhbHVlc19uZWRhLAogICAgICAgICAgICAgICBuYW1lc190byA9ICJuYW1lcyIsIHZhbHVlc190byA9ICJ2YWx1ZXMiKSAlPiUgCiAgbXV0YXRlKG5hbWVzID0gY2FzZV93aGVuKG5hbWVzID09ICJ2YWx1ZXNfYmFzZWxpbmUiIH4gIkJhc2VsaW5lIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgbmFtZXMgPT0gInZhbHVlc19uZWRhIiB+ICJORURBIikpICU+JSAKICBmaWx0ZXIoY2F0ZWcgJWluJSBzZWxfY2F0WzE6MjRdKSAlPiUgCiAgZ2dwbG90KGFlcyh4ID0gZGF5c190d2VldCwgeSA9IHZhbHVlcywgY29sb3IgPSBuYW1lcykpICsKICBnZW9tX2xpbmUoKSArCiAgZmFjZXRfd3JhcChjYXRlZyB+Liwgc2NhbGVzID0gImZyZWUiLCBuY29sID0gNikgKwogIGxhYnMoY29sb3IgPSAiIiwgdGl0bGUgPSAiU2Vjb25kIEFwcHJvYWNoIikgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbj0idG9wIikKYGBgCgojIyBDYXVzYWwgSW1wYWN0IFBhY2thZ2UKSW4gdGhlIHRhYmxlIGJlbGxvdyB3ZSBjb21wYXJlIHJlc3VsdCBmcm9tIGNvbXB1dGluZyBDYXVzYWwgSW1wYWN0IFBhY2thZ2UgdXNpbmcgdGhpcyB0d28gZGlmZmVyZW50IGFwcHJvYWNoLiBPbmx5IHBfdmFsdWUgPCAwLjA1IGFyZSBpbmNsdWRlZC4gQ29sdW1ucyBjb250YWlucyBpbmZvcm1hdGlvbiBhYm91dDoKCjEuIF9fV29yZCBDYXRlZ29yeV9fOiBXb3JkIGNhdGVnb3J5IGluIExJV0MuCgoxLiBfXzFzdCBBcHAgUmVsYXRpdmUgRWZmLl9fOiBDdW1tdWxhdGl2ZSByZWxhdGl2ZSBlZmZlY3QgaW4gcGVyY2VudGFnZXMgd2l0aCB0aGUgZmlyc3QgYXBwcm9hY2guCgoxLiBfXzFzdCBQIFZhbHVlX186IFAgVmFsdWUgZmlyc3QgYXBwcm9hY2guCgoxLiBfXzJuZCBBcHAgUmVsYXRpdmUgRWZmLl9fOiBDdW1tdWxhdGl2ZSByZWxhdGl2ZSBlZmZlY3QgaW4gcGVyY2VudGFnZXMgd2l0aCB0aGUgc2Vjb25kIGFwcHJvYWNoLgoKYGBge3IgfQpkX2ZpcnN0ICU+JSAKICBzZWxlY3QoY2F0ZWcsIHZhbHVlc19uZWRhLCB2YWx1ZXNfYmFzZWxpbmUpICU+JSAKICBuZXN0KGRhdGEgPSAtIGNhdGVnKSAlPiUgCiAgbXV0YXRlKG1vZCA9IG1hcChkYXRhLCB+Q2F1c2FsSW1wYWN0OjpDYXVzYWxJbXBhY3QoLiwgcHJlX3BlcmlvZCwgcG9zdF9wZXJpb2QpKSkgLT4gY2kKICAKY2kgJT4lIAogIG11dGF0ZShzdW1tYXJ5X21vZCA9IG1hcChtb2QsICJzdW1tYXJ5IikpICU+JSAKICBmaWx0ZXIoIW1hcF9sZ2woc3VtbWFyeV9tb2QsIGlzLm51bGwpKSAtPiBjaV9yZXN1bCAKCmNpX3Jlc3VsICU+JSAKICBtdXRhdGUocCA9IG1hcChzdW1tYXJ5X21vZCwgInAiKSkgJT4lIAogIG11dGF0ZShwID0gbWFwX2RibChwLCAxKSkgJT4lIAogIGZpbHRlcihwIDwgMC4wNSkgJT4lIAogIG11dGF0ZShyZWxhdGl2ZV9lZmZlY3QgPSBtYXAoc3VtbWFyeV9tb2QsICJSZWxFZmZlY3QiKSkgJT4lIAogIG11dGF0ZShyZWxhdGl2ZV9lZmZlY3QgPSBtYXBfZGJsKHJlbGF0aXZlX2VmZmVjdCwgMikpLT4gc2lnX2NhdAoKc2lnX2NhdCAlPiUgCiAgZmlsdGVyKGNhdGVnICVpbiUgc2VsX2NhdCkgLT4gc2lnX2NhdAoKc2lnX2NhdCAlPiUgCiAgYXJyYW5nZShkZXNjKHJlbGF0aXZlX2VmZmVjdCkpICU+JSAKICBzZWxlY3QoY2F0ZWcsIGZpcnN0X3JlbGF0aXZlX2VmZmVjdCA9IHJlbGF0aXZlX2VmZmVjdCwgcF9maXJzdCA9IHApIC0+IGZpcnN0X2FwCgpkX3NlY29uZCAlPiUgCiAgc2VsZWN0KGNhdGVnLCB2YWx1ZXNfbmVkYSwgdmFsdWVzX2Jhc2VsaW5lKSAlPiUgCiAgbmVzdChkYXRhID0gLSBjYXRlZykgJT4lIAogIG11dGF0ZShtb2QgPSBtYXAoZGF0YSwgfkNhdXNhbEltcGFjdDo6Q2F1c2FsSW1wYWN0KC4sIHByZV9wZXJpb2QsIHBvc3RfcGVyaW9kKSkpIC0+IGNpCiAgCmNpICU+JSAKICBtdXRhdGUoc3VtbWFyeV9tb2QgPSBtYXAobW9kLCAic3VtbWFyeSIpKSAlPiUgCiAgZmlsdGVyKCFtYXBfbGdsKHN1bW1hcnlfbW9kLCBpcy5udWxsKSkgLT4gY2lfcmVzdWwgCgpjaV9yZXN1bCAlPiUgCiAgbXV0YXRlKHAgPSBtYXAoc3VtbWFyeV9tb2QsICJwIikpICU+JSAKICBtdXRhdGUocCA9IG1hcF9kYmwocCwgMSkpICU+JSAKICBmaWx0ZXIocCA8IDAuMDUpICU+JSAKICBtdXRhdGUocmVsYXRpdmVfZWZmZWN0ID0gbWFwKHN1bW1hcnlfbW9kLCAiUmVsRWZmZWN0IikpICU+JSAKICBtdXRhdGUocmVsYXRpdmVfZWZmZWN0ID0gbWFwX2RibChyZWxhdGl2ZV9lZmZlY3QsIDIpKS0+IHNpZ19jYXQKCnNpZ19jYXQgJT4lIAogIGZpbHRlcihjYXRlZyAlaW4lIHNlbF9jYXQpIC0+IHNpZ19jYXQKCnNpZ19jYXQgJT4lIAogIGFycmFuZ2UoZGVzYyhyZWxhdGl2ZV9lZmZlY3QpKSAlPiUgCiAgc2VsZWN0KGNhdGVnLCBzZWNvbmRfcmVsYXRpdmVfZWZmZWN0ID0gcmVsYXRpdmVfZWZmZWN0LCBwX3NlY29uZCA9IHApIC0+IHNlY29uZF9hcAoKc2Vjb25kX2FwICU+JSAKICBtdXRhdGUoY2F0ZWcgPSBzdHJfdG9fdGl0bGUoY2F0ZWcpKSAlPiUgCiAgbXV0YXRlX2F0KHZhcnMoc2Vjb25kX3JlbGF0aXZlX2VmZmVjdCksIH4uKjEwMCkgJT4lIAogIG11dGF0ZV9pZihpcy5udW1lcmljLCB+cm91bmQoLiwgZGlnaXRzID0gMykpICU+JSAKICBtdXRhdGVfYXQodmFycyhzZWNvbmRfcmVsYXRpdmVfZWZmZWN0KSwgZnVuY3Rpb24oeCl7CiAgICBjZWxsX3NwZWMoeCwgImh0bWwiLCBjb2xvciA9IHNwZWNfY29sb3IoeCksIGJvbGQgPSBUKQogIH0pICU+JSAKICBrYWJsZSgiaHRtbCIsIGVzY2FwZSA9IEYsCiAgICAgICAgYWxpZ24gPSAibHJyIiwKICAgICAgICBjb2wubmFtZXMgPSBjKCJXb3JkIENhdGVnb3J5IiwgIlJlbGF0aXZlIEVmZi4oJSkiLAogICAgICAgICAgICAgICAgICAgICAgIlAgVmFsdWUiKSkgJT4lIAogIGthYmxlX3N0eWxpbmcoYm9vdHN0cmFwX29wdGlvbnMgPSBjKCJzdHJpcGVkIiwgImhvdmVyIiwgImNvbmRlbnNlZCIpLAogICAgICAgICAgICAgICAgZnVsbF93aWR0aCA9IEZBTFNFKQpgYGA=