This program utilizes causal inference methods to estimate the impact of a bad event. Specifically, we estimate the 3-month impact of the 2017 Equifax data breach on stock prices using a synthetic control of competitor and market ETF prices.

Importantly, this skill is highly transferable to other domains. For example, we could estimate the impact of expanding marketing efforts to a new region/country in a non-experimental setting using other untreated countries as a synthetic control.

install.packages("dplyr")
trying URL 'http://cran.rstudio.com/bin/macosx/big-sur-x86_64/contrib/4.4/dplyr_1.1.4.tgz'
Content type 'application/x-gzip' length 1606367 bytes (1.5 MB)
==================================================
downloaded 1.5 MB

The downloaded binary packages are in
    /var/folders/rq/qpfnngq15wj18l5t6y2f0gdm0000gn/T//RtmpRTyaLc/downloaded_packages
install.packages("ggplot2")
trying URL 'http://cran.rstudio.com/bin/macosx/big-sur-x86_64/contrib/4.4/ggplot2_3.5.1.tgz'
Content type 'application/x-gzip' length 4975729 bytes (4.7 MB)
==================================================
downloaded 4.7 MB

The downloaded binary packages are in
    /var/folders/rq/qpfnngq15wj18l5t6y2f0gdm0000gn/T//RtmpRTyaLc/downloaded_packages
install.packages("Synth")
trying URL 'http://cran.rstudio.com/bin/macosx/big-sur-x86_64/contrib/4.4/Synth_1.1-8.tgz'
Content type 'application/x-gzip' length 132221 bytes (129 KB)
==================================================
downloaded 129 KB

The downloaded binary packages are in
    /var/folders/rq/qpfnngq15wj18l5t6y2f0gdm0000gn/T//RtmpRTyaLc/downloaded_packages
library(dplyr)

Attaching package: ‘dplyr’

The following objects are masked from ‘package:stats’:

    filter, lag

The following objects are masked from ‘package:base’:

    intersect, setdiff, setequal, union
library(Synth)
##
## Synth Package: Implements Synthetic Control Methods.

## See https://web.stanford.edu/~jhain/synthpage.html for additional information.
library(ggplot2)

FUNCTIONS CALCULATE MEAN GAP BETWEEN OBSERVED VALUE AND SYNTHETIC CONTROL FOR DEFINED PERIODS

evaluate_gaps <- function(dataprep.out, pre_start, pre_end, post_length) {
  Y1 <- dataprep.out$Y1plot
  Y0_weighted <- dataprep.out$Y0plot %*% synth.out$solution.w
  
  # Calculate the gap
  gap <- Y1 - Y0_weighted
  
  # Combine them into a dataframe
  result_df <- data.frame(
    Y1 = as.vector(Y1),
    Y0_weighted = as.vector(Y0_weighted),
    gap = as.vector(gap)
  )
  
  result_df$index <- as.numeric(rownames(result_df))
  
  if(nrow(result_df) == (post_end-pre_start+1)) {
    post_start <- nrow(result_df) - post_length + 1
    pre_df <- result_df[result_df$index < post_start, ]
    post_df <- result_df[result_df$index >= post_start, ]
    
    print(mean(pre_df$gap))
    print(mean(post_df$gap))
  } 
  else {
    print("Inconsistent length of results")
  }
}

PULL IN PANEL DATA ON STOCK PRICES FROM YAHOO FINANCE

prices <- read.csv('equifax_breach_prices.csv')
prices$date <- as.Date(prices$date)

efx <- prices %>% filter(ticker=='EFX')

ggplot(efx, aes(x = date, y = price, color = ticker)) +
  geom_line() +
  labs(title = "Stock Prices Over Time", x = "Date", y = "Price", color = "Ticker") +
  theme_minimal()



ggplot(prices, aes(x = date, y = price, color = ticker)) +
  geom_line(aes(size = ifelse(ticker == "EFX", "EFX", "Others"))) +
  scale_size_manual(values = c("EFX" = 1.5, "Others" = 0.5), guide='none') +
  labs(title = "Stock Prices Over Time", x = "Date", y = "Price", color = "Ticker") +
  theme_minimal()
Warning: Using `size` aesthetic for lines was deprecated in ggplot2 3.4.0.
ℹ Please use `linewidth` instead.
This warning is displayed once every 8 hours.
Call `lifecycle::last_lifecycle_warnings()` to see where this warning was generated.

PREPARE DATA FOR SYNTHETIC CONTROL: ASSIGN SEQUENTIAL IDs FOR TICKER SYMBOLS AND TRADING DATES

unique_dates <- prices %>%
  select(date) %>%
  distinct() %>%
  arrange(date) %>%
  mutate(period = row_number())
unique_tickers <- prices %>%
  select(ticker) %>%
  distinct() %>%
  mutate(tickerID = ifelse(ticker=='EFX', 1, NA)) %>%
  arrange(tickerID, ticker) %>%
  mutate(tickerID = ifelse(is.na(tickerID), row_number(), tickerID))

df <- prices %>%
  left_join(unique_dates, by = 'date')

df <- df %>%
  left_join(unique_tickers, by = 'ticker')

# Convert group_id to integer
df$tickerID <- as.integer(df$tickerID)
df$period <- as.integer(df$period)

other_columns <- setdiff(names(df), c("tickerID","period"))
df <- df[, c("tickerID","period", other_columns)]

df <- as.data.frame(df)
df <- df %>% filter(!is.na(returns))

DEFINE PARAMETERS AND ESTIMATE SYNTHETIC CONTROL (~22 TRADING DAYS PER MONTH)


event_date <- 215
post_length <- 22*3
pre_length <- 22*3
pre_end <- event_date - 1
pre_start <- pre_end - pre_length 
post_end <- pre_end + post_length 

df1 <- df %>% 
  filter((period >= pre_start) & (period <= post_end))

    dataprep.out <- dataprep(foo = df1,
     time.predictors.prior = pre_start:pre_end, 
     predictors = c('price','returns'),
     predictors.op = 'mean',
     dependent = 'price', # dv
     unit.variable = 'tickerID', #identifying unit numbers
     unit.names.variable = 'ticker', #identifying unit names
     time.variable = 'period', #time-periods
     treatment.identifier = 1, #the treated case
     controls.identifier = 2:8, #the control cases; all others except target
     time.optimize.ssr = pre_start:pre_end, #the time-period over which to optimize
     time.plot = pre_start:post_end) #the entire time period before/after the treatment

# Run the synthetic control analysis
synth.out <- synth(dataprep.out)

X1, X0, Z1, Z0 all come directly from dataprep object.


**************** 
 searching for synthetic control unit  
 

**************** 
**************** 
**************** 

MSPE (LOSS V): 1.715837 

solution.v:
 0.9075486 0.09245137 

solution.w:
 0.06414134 0.06721226 0.2217716 0.3705654 0.0999262 0.08313581 0.09324734 
# Plot the results
path.plot(dataprep.res = dataprep.out, synth.res = synth.out, Ylab = "Outcome", Xlab = "Date")
abline(v = pre_end, col = "red", lwd = 2, lty = 2)  

gaps.plot(dataprep.res = dataprep.out, synth.res = synth.out, Ylab = "Gap in Outcome", Xlab = "Date")
abline(v = pre_end, col = "red", lwd = 2, lty = 2)  


print(dataprep.out$names.and.numbers)


evaluate_gaps(dataprep.out, pre_start, pre_end, post_length)
[1] 1.51598e-06
[1] -39.15297

PLACEBO TESTING: IF UNBIASED, RESULTS SHOULD BE MUCH CLOSER TO ZERO


post_length <- 22*3
pre_length <- 22*3
post_end <- event_date - 1 
pre_end <- post_end - post_length + 1
pre_start <- pre_end - pre_length 

aa <- df %>% 
  filter((period >= pre_start) & (period <= post_end))

    dataprep.out <- dataprep(foo = aa,
     time.predictors.prior = pre_start:pre_end, 
     predictors = c('price','returns'),
     predictors.op = 'mean',
     dependent = 'price', # dv
     unit.variable = 'tickerID', #identifying unit numbers
     unit.names.variable = 'ticker', #identifying unit names
     time.variable = 'period', #time-periods
     treatment.identifier = 1, #the treated case
     controls.identifier = 2:8, #the control cases; all others except target
     time.optimize.ssr = pre_start:pre_end, #the time-period over which to optimize
     time.plot = pre_start:post_end) #the entire time period before/after the treatment

# Run the synthetic control analysis
synth.out <- synth(dataprep.out)

X1, X0, Z1, Z0 all come directly from dataprep object.


**************** 
 searching for synthetic control unit  
 

**************** 
**************** 
**************** 

MSPE (LOSS V): 2.890948 

solution.v:
 1 0 

solution.w:
 0.08772161 0.083939 0.112727 0.4109511 0.08952511 0.1018565 0.1132797 
# Plot the results
path.plot(dataprep.res = dataprep.out, synth.res = synth.out, Ylab = "Outcome", Xlab = "Date")
abline(v = pre_end, col = "red", lwd = 2, lty = 2)  

gaps.plot(dataprep.res = dataprep.out, synth.res = synth.out, Ylab = "Gap in Outcome", Xlab = "Date")
abline(v = pre_end, col = "red", lwd = 2, lty = 2)  


print(dataprep.out$names.and.numbers)

evaluate_gaps(dataprep.out, pre_start, pre_end, post_length)
[1] 0.02647449
[1] -0.1771741
LS0tCnRpdGxlOiAiRXF1aWZheCBCcmVhY2ggU3ludGhldGljIENvbnRyb2wiCmF1dGhvcjogIkpvbmF0aGFuIEhlcnNoYWZmIgpkYXRlOiAiMjAyNC0wNi0wMSIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQoKCiMjIyMgVGhpcyBwcm9ncmFtIHV0aWxpemVzIGNhdXNhbCBpbmZlcmVuY2UgbWV0aG9kcyB0byBlc3RpbWF0ZSB0aGUgaW1wYWN0IG9mIGEgYmFkIGV2ZW50LiBTcGVjaWZpY2FsbHksIHdlIGVzdGltYXRlIHRoZSAzLW1vbnRoIGltcGFjdCBvZiB0aGUgMjAxNyBFcXVpZmF4IGRhdGEgYnJlYWNoIG9uIHN0b2NrIHByaWNlcyB1c2luZyBhIHN5bnRoZXRpYyBjb250cm9sIG9mIGNvbXBldGl0b3IgYW5kIG1hcmtldCBFVEYgcHJpY2VzLiAKCiMjIyMgSW1wb3J0YW50bHksIHRoaXMgc2tpbGwgaXMgaGlnaGx5IHRyYW5zZmVyYWJsZSB0byBvdGhlciBkb21haW5zLiBGb3IgZXhhbXBsZSwgd2UgY291bGQgZXN0aW1hdGUgdGhlIGltcGFjdCBvZiBleHBhbmRpbmcgbWFya2V0aW5nIGVmZm9ydHMgdG8gYSBuZXcgcmVnaW9uL2NvdW50cnkgaW4gYSBub24tZXhwZXJpbWVudGFsIHNldHRpbmcgdXNpbmcgb3RoZXIgdW50cmVhdGVkIGNvdW50cmllcyBhcyBhIHN5bnRoZXRpYyBjb250cm9sLiAKCgpgYGB7ciBpbnN0YWxsLXBhY2thZ2VzfQppbnN0YWxsLnBhY2thZ2VzKCJkcGx5ciIpCmluc3RhbGwucGFja2FnZXMoImdncGxvdDIiKQppbnN0YWxsLnBhY2thZ2VzKCJTeW50aCIpCmxpYnJhcnkoZHBseXIpCmxpYnJhcnkoU3ludGgpCmxpYnJhcnkoZ2dwbG90MikKYGBgCgojIyMjIEZVTkNUSU9OUyBDQUxDVUxBVEUgTUVBTiBHQVAgQkVUV0VFTiBPQlNFUlZFRCBWQUxVRSBBTkQgU1lOVEhFVElDIENPTlRST0wgRk9SIERFRklORUQgUEVSSU9EUwoKYGBge3IgZnVuY3Rpb25zfQpldmFsdWF0ZV9nYXBzIDwtIGZ1bmN0aW9uKGRhdGFwcmVwLm91dCwgcHJlX3N0YXJ0LCBwcmVfZW5kLCBwb3N0X2xlbmd0aCkgewogIFkxIDwtIGRhdGFwcmVwLm91dCRZMXBsb3QKICBZMF93ZWlnaHRlZCA8LSBkYXRhcHJlcC5vdXQkWTBwbG90ICUqJSBzeW50aC5vdXQkc29sdXRpb24udwogIAogICMgQ2FsY3VsYXRlIHRoZSBnYXAKICBnYXAgPC0gWTEgLSBZMF93ZWlnaHRlZAogIAogICMgQ29tYmluZSB0aGVtIGludG8gYSBkYXRhZnJhbWUKICByZXN1bHRfZGYgPC0gZGF0YS5mcmFtZSgKICAgIFkxID0gYXMudmVjdG9yKFkxKSwKICAgIFkwX3dlaWdodGVkID0gYXMudmVjdG9yKFkwX3dlaWdodGVkKSwKICAgIGdhcCA9IGFzLnZlY3RvcihnYXApCiAgKQogIAogIHJlc3VsdF9kZiRpbmRleCA8LSBhcy5udW1lcmljKHJvd25hbWVzKHJlc3VsdF9kZikpCiAgCiAgaWYobnJvdyhyZXN1bHRfZGYpID09IChwb3N0X2VuZC1wcmVfc3RhcnQrMSkpIHsKICAgIHBvc3Rfc3RhcnQgPC0gbnJvdyhyZXN1bHRfZGYpIC0gcG9zdF9sZW5ndGggKyAxCiAgICBwcmVfZGYgPC0gcmVzdWx0X2RmW3Jlc3VsdF9kZiRpbmRleCA8IHBvc3Rfc3RhcnQsIF0KICAgIHBvc3RfZGYgPC0gcmVzdWx0X2RmW3Jlc3VsdF9kZiRpbmRleCA+PSBwb3N0X3N0YXJ0LCBdCiAgICAKICAgIHByaW50KG1lYW4ocHJlX2RmJGdhcCkpCiAgICBwcmludChtZWFuKHBvc3RfZGYkZ2FwKSkKICB9IAogIGVsc2UgewogICAgcHJpbnQoIkluY29uc2lzdGVudCBsZW5ndGggb2YgcmVzdWx0cyIpCiAgfQp9CmBgYAoKCiMjIyMgUFVMTCBJTiBQQU5FTCBEQVRBIE9OIFNUT0NLIFBSSUNFUyBGUk9NIFlBSE9PIEZJTkFOQ0UKCmBgYHtyIGltcG9ydC1zdG9jay1wcmljZS1wYW5lbH0KcHJpY2VzIDwtIHJlYWQuY3N2KCdlcXVpZmF4X2JyZWFjaF9wcmljZXMuY3N2JykKcHJpY2VzJGRhdGUgPC0gYXMuRGF0ZShwcmljZXMkZGF0ZSkKCmVmeCA8LSBwcmljZXMgJT4lIGZpbHRlcih0aWNrZXI9PSdFRlgnKQoKZ2dwbG90KGVmeCwgYWVzKHggPSBkYXRlLCB5ID0gcHJpY2UsIGNvbG9yID0gdGlja2VyKSkgKwogIGdlb21fbGluZSgpICsKICBsYWJzKHRpdGxlID0gIlN0b2NrIFByaWNlcyBPdmVyIFRpbWUiLCB4ID0gIkRhdGUiLCB5ID0gIlByaWNlIiwgY29sb3IgPSAiVGlja2VyIikgKwogIHRoZW1lX21pbmltYWwoKQoKCmdncGxvdChwcmljZXMsIGFlcyh4ID0gZGF0ZSwgeSA9IHByaWNlLCBjb2xvciA9IHRpY2tlcikpICsKICBnZW9tX2xpbmUoYWVzKHNpemUgPSBpZmVsc2UodGlja2VyID09ICJFRlgiLCAiRUZYIiwgIk90aGVycyIpKSkgKwogIHNjYWxlX3NpemVfbWFudWFsKHZhbHVlcyA9IGMoIkVGWCIgPSAxLjUsICJPdGhlcnMiID0gMC41KSwgZ3VpZGU9J25vbmUnKSArCiAgbGFicyh0aXRsZSA9ICJTdG9jayBQcmljZXMgT3ZlciBUaW1lIiwgeCA9ICJEYXRlIiwgeSA9ICJQcmljZSIsIGNvbG9yID0gIlRpY2tlciIpICsKICB0aGVtZV9taW5pbWFsKCkKCgpgYGAKCgojIyMjIFBSRVBBUkUgREFUQSBGT1IgU1lOVEhFVElDIENPTlRST0w6IEFTU0lHTiBTRVFVRU5USUFMIElEcyBGT1IgVElDS0VSIFNZTUJPTFMgQU5EIFRSQURJTkcgREFURVMKCmBgYHtyIHVuaXF1ZS1wZXJpb2RJRC1mb3ItZWFjaC1kYXRlfQp1bmlxdWVfZGF0ZXMgPC0gcHJpY2VzICU+JQogIHNlbGVjdChkYXRlKSAlPiUKICBkaXN0aW5jdCgpICU+JQogIGFycmFuZ2UoZGF0ZSkgJT4lCiAgbXV0YXRlKHBlcmlvZCA9IHJvd19udW1iZXIoKSkKCmBgYAoKYGBge3IgdW5pcXVlLXRpY2tlcklELWZvci1lYWNoLXRpY2tlcn0KdW5pcXVlX3RpY2tlcnMgPC0gcHJpY2VzICU+JQogIHNlbGVjdCh0aWNrZXIpICU+JQogIGRpc3RpbmN0KCkgJT4lCiAgbXV0YXRlKHRpY2tlcklEID0gaWZlbHNlKHRpY2tlcj09J0VGWCcsIDEsIE5BKSkgJT4lCiAgYXJyYW5nZSh0aWNrZXJJRCwgdGlja2VyKSAlPiUKICBtdXRhdGUodGlja2VySUQgPSBpZmVsc2UoaXMubmEodGlja2VySUQpLCByb3dfbnVtYmVyKCksIHRpY2tlcklEKSkKYGBgCgoKYGBge3Igam9pbi11bmlxdWUtSURzfQoKZGYgPC0gcHJpY2VzICU+JQogIGxlZnRfam9pbih1bmlxdWVfZGF0ZXMsIGJ5ID0gJ2RhdGUnKQoKZGYgPC0gZGYgJT4lCiAgbGVmdF9qb2luKHVuaXF1ZV90aWNrZXJzLCBieSA9ICd0aWNrZXInKQoKIyBDb252ZXJ0IGdyb3VwX2lkIHRvIGludGVnZXIKZGYkdGlja2VySUQgPC0gYXMuaW50ZWdlcihkZiR0aWNrZXJJRCkKZGYkcGVyaW9kIDwtIGFzLmludGVnZXIoZGYkcGVyaW9kKQoKb3RoZXJfY29sdW1ucyA8LSBzZXRkaWZmKG5hbWVzKGRmKSwgYygidGlja2VySUQiLCJwZXJpb2QiKSkKZGYgPC0gZGZbLCBjKCJ0aWNrZXJJRCIsInBlcmlvZCIsIG90aGVyX2NvbHVtbnMpXQoKZGYgPC0gYXMuZGF0YS5mcmFtZShkZikKZGYgPC0gZGYgJT4lIGZpbHRlcighaXMubmEocmV0dXJucykpCgpgYGAKCgojIyMjIERFRklORSBQQVJBTUVURVJTIEFORCBFU1RJTUFURSBTWU5USEVUSUMgQ09OVFJPTCAofjIyIFRSQURJTkcgREFZUyBQRVIgTU9OVEgpCgpgYGAge3IgU3ludGh9IAoKZXZlbnRfZGF0ZSA8LSAyMTUKcG9zdF9sZW5ndGggPC0gMjIqMwpwcmVfbGVuZ3RoIDwtIDIyKjMKcHJlX2VuZCA8LSBldmVudF9kYXRlIC0gMQpwcmVfc3RhcnQgPC0gcHJlX2VuZCAtIHByZV9sZW5ndGggCnBvc3RfZW5kIDwtIHByZV9lbmQgKyBwb3N0X2xlbmd0aCAKCmRmMSA8LSBkZiAlPiUgCiAgZmlsdGVyKChwZXJpb2QgPj0gcHJlX3N0YXJ0KSAmIChwZXJpb2QgPD0gcG9zdF9lbmQpKQoKICAgIGRhdGFwcmVwLm91dCA8LSBkYXRhcHJlcChmb28gPSBkZjEsCiAgICAgdGltZS5wcmVkaWN0b3JzLnByaW9yID0gcHJlX3N0YXJ0OnByZV9lbmQsIAogICAgIHByZWRpY3RvcnMgPSBjKCdwcmljZScsJ3JldHVybnMnKSwKICAgICBwcmVkaWN0b3JzLm9wID0gJ21lYW4nLAogICAgIGRlcGVuZGVudCA9ICdwcmljZScsICMgZHYKICAgICB1bml0LnZhcmlhYmxlID0gJ3RpY2tlcklEJywgI2lkZW50aWZ5aW5nIHVuaXQgbnVtYmVycwogICAgIHVuaXQubmFtZXMudmFyaWFibGUgPSAndGlja2VyJywgI2lkZW50aWZ5aW5nIHVuaXQgbmFtZXMKICAgICB0aW1lLnZhcmlhYmxlID0gJ3BlcmlvZCcsICN0aW1lLXBlcmlvZHMKICAgICB0cmVhdG1lbnQuaWRlbnRpZmllciA9IDEsICN0aGUgdHJlYXRlZCBjYXNlCiAgICAgY29udHJvbHMuaWRlbnRpZmllciA9IDI6OCwgI3RoZSBjb250cm9sIGNhc2VzOyBhbGwgb3RoZXJzIGV4Y2VwdCB0YXJnZXQKICAgICB0aW1lLm9wdGltaXplLnNzciA9IHByZV9zdGFydDpwcmVfZW5kLCAjdGhlIHRpbWUtcGVyaW9kIG92ZXIgd2hpY2ggdG8gb3B0aW1pemUKICAgICB0aW1lLnBsb3QgPSBwcmVfc3RhcnQ6cG9zdF9lbmQpICN0aGUgZW50aXJlIHRpbWUgcGVyaW9kIGJlZm9yZS9hZnRlciB0aGUgdHJlYXRtZW50CgojIFJ1biB0aGUgc3ludGhldGljIGNvbnRyb2wgYW5hbHlzaXMKc3ludGgub3V0IDwtIHN5bnRoKGRhdGFwcmVwLm91dCkKCiMgUGxvdCB0aGUgcmVzdWx0cwpwYXRoLnBsb3QoZGF0YXByZXAucmVzID0gZGF0YXByZXAub3V0LCBzeW50aC5yZXMgPSBzeW50aC5vdXQsIFlsYWIgPSAiT3V0Y29tZSIsIFhsYWIgPSAiRGF0ZSIpCmFibGluZSh2ID0gcHJlX2VuZCwgY29sID0gInJlZCIsIGx3ZCA9IDIsIGx0eSA9IDIpICAKZ2Fwcy5wbG90KGRhdGFwcmVwLnJlcyA9IGRhdGFwcmVwLm91dCwgc3ludGgucmVzID0gc3ludGgub3V0LCBZbGFiID0gIkdhcCBpbiBPdXRjb21lIiwgWGxhYiA9ICJEYXRlIikKYWJsaW5lKHYgPSBwcmVfZW5kLCBjb2wgPSAicmVkIiwgbHdkID0gMiwgbHR5ID0gMikgIAoKcHJpbnQoZGF0YXByZXAub3V0JG5hbWVzLmFuZC5udW1iZXJzKQoKCmV2YWx1YXRlX2dhcHMoZGF0YXByZXAub3V0LCBwcmVfc3RhcnQsIHByZV9lbmQsIHBvc3RfbGVuZ3RoKQoKCmBgYAoKIyMjIyBQTEFDRUJPIFRFU1RJTkc6IElGIFVOQklBU0VELCBSRVNVTFRTIFNIT1VMRCBCRSBNVUNIIENMT1NFUiBUTyBaRVJPCgpgYGB7ciBBQS10ZXN0fQoKcG9zdF9sZW5ndGggPC0gMjIqMwpwcmVfbGVuZ3RoIDwtIDIyKjMKcG9zdF9lbmQgPC0gZXZlbnRfZGF0ZSAtIDEgCnByZV9lbmQgPC0gcG9zdF9lbmQgLSBwb3N0X2xlbmd0aCArIDEKcHJlX3N0YXJ0IDwtIHByZV9lbmQgLSBwcmVfbGVuZ3RoIAoKYWEgPC0gZGYgJT4lIAogIGZpbHRlcigocGVyaW9kID49IHByZV9zdGFydCkgJiAocGVyaW9kIDw9IHBvc3RfZW5kKSkKCiAgICBkYXRhcHJlcC5vdXQgPC0gZGF0YXByZXAoZm9vID0gYWEsCiAgICAgdGltZS5wcmVkaWN0b3JzLnByaW9yID0gcHJlX3N0YXJ0OnByZV9lbmQsIAogICAgIHByZWRpY3RvcnMgPSBjKCdwcmljZScsJ3JldHVybnMnKSwKICAgICBwcmVkaWN0b3JzLm9wID0gJ21lYW4nLAogICAgIGRlcGVuZGVudCA9ICdwcmljZScsICMgZHYKICAgICB1bml0LnZhcmlhYmxlID0gJ3RpY2tlcklEJywgI2lkZW50aWZ5aW5nIHVuaXQgbnVtYmVycwogICAgIHVuaXQubmFtZXMudmFyaWFibGUgPSAndGlja2VyJywgI2lkZW50aWZ5aW5nIHVuaXQgbmFtZXMKICAgICB0aW1lLnZhcmlhYmxlID0gJ3BlcmlvZCcsICN0aW1lLXBlcmlvZHMKICAgICB0cmVhdG1lbnQuaWRlbnRpZmllciA9IDEsICN0aGUgdHJlYXRlZCBjYXNlCiAgICAgY29udHJvbHMuaWRlbnRpZmllciA9IDI6OCwgI3RoZSBjb250cm9sIGNhc2VzOyBhbGwgb3RoZXJzIGV4Y2VwdCB0YXJnZXQKICAgICB0aW1lLm9wdGltaXplLnNzciA9IHByZV9zdGFydDpwcmVfZW5kLCAjdGhlIHRpbWUtcGVyaW9kIG92ZXIgd2hpY2ggdG8gb3B0aW1pemUKICAgICB0aW1lLnBsb3QgPSBwcmVfc3RhcnQ6cG9zdF9lbmQpICN0aGUgZW50aXJlIHRpbWUgcGVyaW9kIGJlZm9yZS9hZnRlciB0aGUgdHJlYXRtZW50CgojIFJ1biB0aGUgc3ludGhldGljIGNvbnRyb2wgYW5hbHlzaXMKc3ludGgub3V0IDwtIHN5bnRoKGRhdGFwcmVwLm91dCkKCiMgUGxvdCB0aGUgcmVzdWx0cwpwYXRoLnBsb3QoZGF0YXByZXAucmVzID0gZGF0YXByZXAub3V0LCBzeW50aC5yZXMgPSBzeW50aC5vdXQsIFlsYWIgPSAiT3V0Y29tZSIsIFhsYWIgPSAiRGF0ZSIpCmFibGluZSh2ID0gcHJlX2VuZCwgY29sID0gInJlZCIsIGx3ZCA9IDIsIGx0eSA9IDIpICAKZ2Fwcy5wbG90KGRhdGFwcmVwLnJlcyA9IGRhdGFwcmVwLm91dCwgc3ludGgucmVzID0gc3ludGgub3V0LCBZbGFiID0gIkdhcCBpbiBPdXRjb21lIiwgWGxhYiA9ICJEYXRlIikKYWJsaW5lKHYgPSBwcmVfZW5kLCBjb2wgPSAicmVkIiwgbHdkID0gMiwgbHR5ID0gMikgIAoKcHJpbnQoZGF0YXByZXAub3V0JG5hbWVzLmFuZC5udW1iZXJzKQoKZXZhbHVhdGVfZ2FwcyhkYXRhcHJlcC5vdXQsIHByZV9zdGFydCwgcHJlX2VuZCwgcG9zdF9sZW5ndGgpCgpgYGAKCgo=