Loading libraries

library(tidyverse)
library(highcharter)
library(quantmod)
library(doParallel)
library(lubridate)

registerDoParallel(cores = 10)
options(scipen = 100)

Helper functions

For future summaries/transformations, implied correlation equation at the bottom of the markdown

# Helper Functions

divide <- function(x){
  x/100
}

# implied correlation equation: more about this at the end of the markdown

imp_corr_value <- function(x = x, comp_data = comp_data, factor = "iv10d"){
  
  adj_weight = x$weight/sum(x$weight)
  ticker = unique(x$composite_ticker)
  
  etf_data = filter(comp_data, composite_ticker == ticker)
  etf_var = etf_data[,factor]^2
  
  value = etf_var/(sum(x[,factor]*adj_weight)^2)
  return(value)
}

imp_corr_tbl <- function(x, comp_data){
  
  ticker = unique(x$composite_ticker)
  
  df = data.frame(
    ticker = ticker,
    dte10 = imp_corr_value(x = x, comp_data = comp_data),
    dte20 = imp_corr_value(x = x, comp_data = comp_data, factor = "iv20d"),
    dte30 = imp_corr_value(x = x, comp_data = comp_data, factor = "iv30d"),
    dte60 = imp_corr_value(x = x, comp_data = comp_data, factor = "iv60d"),
    dte90 = imp_corr_value(x = x, comp_data = comp_data, factor = "iv90d"),
    dte6m = imp_corr_value(x = x, comp_data = comp_data, factor = "iv6m"),
    dte1yr = imp_corr_value(x = x, comp_data = comp_data, factor = "iv1yr"),
    row.names = NULL
  )
  return(df)
}

Downloading Data

holdings_raw has all the holdings and corresponding weights for each etf of “2021-11-01”. quandl_data has all the vol data. vol_data is subsetting the implied vol dte’s i’m going to use for this table. the data comes with vol*100, so i transformed the vol data back to it’s percentage with the divide helper function

# Downloading Data

holdings_raw <- read_csv("holdings_data.csv")
quandl_data <- read_csv("quandl_data.csv")

vol_data <- quandl_data %>%
  select(ticker, tradedate, iv10d, iv20d, iv30d, iv60d, iv90d, iv6m, iv1yr) %>% 
  mutate_if(is.numeric, divide) %>%
  arrange(ticker)

holdings_raw data (first 100 rows)

vol_data data

# Transforms

# Filtering for tickers that quandl has vol data for
holdings_raw <- na.omit(holdings_raw[,2:5]) %>%
  mutate(as_of_date = ymd(as_of_date)) %>%
  filter(
    composite_ticker %in% vol_data$ticker,
    constituent_ticker %in% vol_data$ticker
  )

# Filtering out ETFs with summed weights of less than 50%
comp_tickers_weight_filtered <- holdings_raw %>%
  group_by(composite_ticker) %>%
  summarise(value = sum(weight)) %>%
  arrange(desc(value)) %>%
  filter(value >= 0.50) %>%
  pull(composite_ticker)

holdings_df <- holdings_raw %>%
  filter(
    composite_ticker %in% comp_tickers_weight_filtered
  ) %>%
  select(-c(as_of_date))


# Separating ETF data
comp_tickers = holdings_df %>%
  select(-c(constituent_ticker, weight)) %>%
  distinct()

holdings_df

const_data: merged dataframe of constituent tickers and it’s corresponding iv + weight values

implied correlation summaries

mapped heped function imp_cprr_tabl to the listed_data. then innerjoined the resulting dataframe with a new column called “sum_weights”, this tells us the sum weights of the holdings that i had vol data for (and applied the imp correlation with)

merging data

sourcing composite (etf) name, merging all dataframes


metrics <- yahooQF(c("Name", "Change in Percent"))

tickers_yf = getQuote(df$ticker, what = metrics) %>%
  mutate(ticker = rownames(.),
         Name = ifelse(is.na(Name), ticker, Name))

ticker_names <- tickers_yf %>%
  mutate(ticker = rownames(tickers_yf)) %>%
  select(ticker, everything())

final = df %>%
  left_join(ticker_names[,c("ticker", "Name")], by = c("ticker")) %>%
  mutate(date = "2021-11-01") %>%
  select(ticker, Name, date, pxatmiv, everything()) %>%
  rename("Price" = pxatmiv)

final

cleaning df

added ‘leveraged’ column, string detected for “2”, “3”, “ultra”, or “direxion” and labeled them TRUE to filter out leveraged etfs. changed all column name to lower-case, and renamed the imp corr column values to their corresponding ivol[dte]

imp corr equation used

x = constituent data. comp_data = composite (etf) data. factor = ivol[dte] used.

adj_weight = constituent weight divided by the sum of all constituent weights (some etfs vary in the amount of weight data available. so this is to adjusted it so that an initial sum of 70% weight = 100% weight)

ticker = composite (etf) tickers

etf_data = composite (etf) vol data

etf_var = etf variance for selected factor (ie. SPY iv[dte]^2)

value = imp corr equation Caption for the picture.

etf variance/sum(constituent vol * constituent adjusted_weight)^2

imp_corr_value <- function(x = x, comp_data = comp_data, factor = "iv10d"){
  
  adj_weight = x$weight/sum(x$weight)
  ticker = unique(x$composite_ticker)
  
  etf_data = filter(comp_data, composite_ticker == ticker)
  etf_var = etf_data[,factor]^2
  
  value = etf_var/(sum(x[,factor]*adj_weight)^2)
  return(value)
}
LS0tDQp0aXRsZTogIkltcGxpZWQgQ29ycmVsYXRpb24gVGFibGUiDQpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sNCi0tLQ0KDQojIExvYWRpbmcgbGlicmFyaWVzDQoNCmBgYHtyfQ0KbGlicmFyeSh0aWR5dmVyc2UpDQpsaWJyYXJ5KGhpZ2hjaGFydGVyKQ0KbGlicmFyeShxdWFudG1vZCkNCmxpYnJhcnkoZG9QYXJhbGxlbCkNCmxpYnJhcnkobHVicmlkYXRlKQ0KDQpyZWdpc3RlckRvUGFyYWxsZWwoY29yZXMgPSAxMCkNCm9wdGlvbnMoc2NpcGVuID0gMTAwKQ0KYGBgDQoNCiMgSGVscGVyIGZ1bmN0aW9ucw0KRm9yIGZ1dHVyZSBzdW1tYXJpZXMvdHJhbnNmb3JtYXRpb25zLCBpbXBsaWVkIGNvcnJlbGF0aW9uIGVxdWF0aW9uIGF0IHRoZSBib3R0b20gb2YgdGhlIG1hcmtkb3duDQoNCmBgYHtyLCBlY2hvPVRSVUV9DQojIEhlbHBlciBGdW5jdGlvbnMNCg0KZGl2aWRlIDwtIGZ1bmN0aW9uKHgpew0KICB4LzEwMA0KfQ0KDQojIGltcGxpZWQgY29ycmVsYXRpb24gZXF1YXRpb246IG1vcmUgYWJvdXQgdGhpcyBhdCB0aGUgZW5kIG9mIHRoZSBtYXJrZG93bg0KDQppbXBfY29ycl92YWx1ZSA8LSBmdW5jdGlvbih4ID0geCwgY29tcF9kYXRhID0gY29tcF9kYXRhLCBmYWN0b3IgPSAiaXYxMGQiKXsNCiAgDQogIGFkal93ZWlnaHQgPSB4JHdlaWdodC9zdW0oeCR3ZWlnaHQpDQogIHRpY2tlciA9IHVuaXF1ZSh4JGNvbXBvc2l0ZV90aWNrZXIpDQogIA0KICBldGZfZGF0YSA9IGZpbHRlcihjb21wX2RhdGEsIGNvbXBvc2l0ZV90aWNrZXIgPT0gdGlja2VyKQ0KICBldGZfdmFyID0gZXRmX2RhdGFbLGZhY3Rvcl1eMg0KICANCiAgdmFsdWUgPSBldGZfdmFyLyhzdW0oeFssZmFjdG9yXSphZGpfd2VpZ2h0KV4yKQ0KICByZXR1cm4odmFsdWUpDQp9DQoNCmltcF9jb3JyX3RibCA8LSBmdW5jdGlvbih4LCBjb21wX2RhdGEpew0KICANCiAgdGlja2VyID0gdW5pcXVlKHgkY29tcG9zaXRlX3RpY2tlcikNCiAgDQogIGRmID0gZGF0YS5mcmFtZSgNCiAgICB0aWNrZXIgPSB0aWNrZXIsDQogICAgZHRlMTAgPSBpbXBfY29ycl92YWx1ZSh4ID0geCwgY29tcF9kYXRhID0gY29tcF9kYXRhKSwNCiAgICBkdGUyMCA9IGltcF9jb3JyX3ZhbHVlKHggPSB4LCBjb21wX2RhdGEgPSBjb21wX2RhdGEsIGZhY3RvciA9ICJpdjIwZCIpLA0KICAgIGR0ZTMwID0gaW1wX2NvcnJfdmFsdWUoeCA9IHgsIGNvbXBfZGF0YSA9IGNvbXBfZGF0YSwgZmFjdG9yID0gIml2MzBkIiksDQogICAgZHRlNjAgPSBpbXBfY29ycl92YWx1ZSh4ID0geCwgY29tcF9kYXRhID0gY29tcF9kYXRhLCBmYWN0b3IgPSAiaXY2MGQiKSwNCiAgICBkdGU5MCA9IGltcF9jb3JyX3ZhbHVlKHggPSB4LCBjb21wX2RhdGEgPSBjb21wX2RhdGEsIGZhY3RvciA9ICJpdjkwZCIpLA0KICAgIGR0ZTZtID0gaW1wX2NvcnJfdmFsdWUoeCA9IHgsIGNvbXBfZGF0YSA9IGNvbXBfZGF0YSwgZmFjdG9yID0gIml2Nm0iKSwNCiAgICBkdGUxeXIgPSBpbXBfY29ycl92YWx1ZSh4ID0geCwgY29tcF9kYXRhID0gY29tcF9kYXRhLCBmYWN0b3IgPSAiaXYxeXIiKSwNCiAgICByb3cubmFtZXMgPSBOVUxMDQogICkNCiAgcmV0dXJuKGRmKQ0KfQ0KYGBgDQoNCiMgRG93bmxvYWRpbmcgRGF0YQ0KaG9sZGluZ3NfcmF3IGhhcyBhbGwgdGhlIGhvbGRpbmdzIGFuZCBjb3JyZXNwb25kaW5nIHdlaWdodHMgZm9yIGVhY2ggZXRmIG9mICIyMDIxLTExLTAxIi4gcXVhbmRsX2RhdGEgaGFzIGFsbCB0aGUgdm9sIGRhdGEuIHZvbF9kYXRhIGlzIHN1YnNldHRpbmcgdGhlIGltcGxpZWQgdm9sIGR0ZSdzIGknbSBnb2luZyB0byB1c2UgZm9yIHRoaXMgdGFibGUuIHRoZSBkYXRhIGNvbWVzIHdpdGggdm9sKjEwMCwgc28gaSB0cmFuc2Zvcm1lZCB0aGUgdm9sIGRhdGEgYmFjayB0byBpdCdzIHBlcmNlbnRhZ2Ugd2l0aCB0aGUgZGl2aWRlIGhlbHBlciBmdW5jdGlvbg0KDQpgYGB7ciBlY2hvPVRSVUV9DQojIERvd25sb2FkaW5nIERhdGENCg0KaG9sZGluZ3NfcmF3IDwtIHJlYWRfY3N2KCJob2xkaW5nc19kYXRhLmNzdiIpDQpxdWFuZGxfZGF0YSA8LSByZWFkX2NzdigicXVhbmRsX2RhdGEuY3N2IikNCg0Kdm9sX2RhdGEgPC0gcXVhbmRsX2RhdGEgJT4lDQogIHNlbGVjdCh0aWNrZXIsIHRyYWRlZGF0ZSwgaXYxMGQsIGl2MjBkLCBpdjMwZCwgaXY2MGQsIGl2OTBkLCBpdjZtLCBpdjF5cikgJT4lIA0KICBtdXRhdGVfaWYoaXMubnVtZXJpYywgZGl2aWRlKSAlPiUNCiAgYXJyYW5nZSh0aWNrZXIpDQpgYGANCg0KIyBob2xkaW5nc19yYXcgZGF0YSAoZmlyc3QgMTAwIHJvd3MpDQoNCmBgYHtyfQ0KaG9sZGluZ3NfcmF3WzE6MTAwLF0NCmBgYA0KDQojIHZvbF9kYXRhIGRhdGENCg0KYGBge3J9DQp2b2xfZGF0YQ0KYGBgDQoNCmBgYHtyfQ0KIyBUcmFuc2Zvcm1zDQoNCiMgRmlsdGVyaW5nIGZvciB0aWNrZXJzIHRoYXQgcXVhbmRsIGhhcyB2b2wgZGF0YSBmb3INCmhvbGRpbmdzX3JhdyA8LSBuYS5vbWl0KGhvbGRpbmdzX3Jhd1ssMjo1XSkgJT4lDQogIG11dGF0ZShhc19vZl9kYXRlID0geW1kKGFzX29mX2RhdGUpKSAlPiUNCiAgZmlsdGVyKA0KICAgIGNvbXBvc2l0ZV90aWNrZXIgJWluJSB2b2xfZGF0YSR0aWNrZXIsDQogICAgY29uc3RpdHVlbnRfdGlja2VyICVpbiUgdm9sX2RhdGEkdGlja2VyDQogICkNCg0KIyBGaWx0ZXJpbmcgb3V0IEVURnMgd2l0aCBzdW1tZWQgd2VpZ2h0cyBvZiBsZXNzIHRoYW4gNTAlDQpjb21wX3RpY2tlcnNfd2VpZ2h0X2ZpbHRlcmVkIDwtIGhvbGRpbmdzX3JhdyAlPiUNCiAgZ3JvdXBfYnkoY29tcG9zaXRlX3RpY2tlcikgJT4lDQogIHN1bW1hcmlzZSh2YWx1ZSA9IHN1bSh3ZWlnaHQpKSAlPiUNCiAgYXJyYW5nZShkZXNjKHZhbHVlKSkgJT4lDQogIGZpbHRlcih2YWx1ZSA+PSAwLjUwKSAlPiUNCiAgcHVsbChjb21wb3NpdGVfdGlja2VyKQ0KDQpob2xkaW5nc19kZiA8LSBob2xkaW5nc19yYXcgJT4lDQogIGZpbHRlcigNCiAgICBjb21wb3NpdGVfdGlja2VyICVpbiUgY29tcF90aWNrZXJzX3dlaWdodF9maWx0ZXJlZA0KICApICU+JQ0KICBzZWxlY3QoLWMoYXNfb2ZfZGF0ZSkpDQoNCg0KIyBTZXBhcmF0aW5nIEVURiBkYXRhDQpjb21wX3RpY2tlcnMgPSBob2xkaW5nc19kZiAlPiUNCiAgc2VsZWN0KC1jKGNvbnN0aXR1ZW50X3RpY2tlciwgd2VpZ2h0KSkgJT4lDQogIGRpc3RpbmN0KCkNCg0KYGBgDQoNCmhvbGRpbmdzX2RmDQoNCmBgYHtyfQ0KaG9sZGluZ3NfZGYNCmBgYA0KDQpgYGB7cn0NCg0KIyBKb2luaW5nIGhvbGRpbmdzIGRhdGEgd2l0aCB2b2wgZGF0YQ0KDQpjb21wX2RhdGEgPC0gY29tcF90aWNrZXJzICU+JQ0KICBpbm5lcl9qb2luKHZvbF9kYXRhLCBieSA9IGMoImNvbXBvc2l0ZV90aWNrZXIiID0gInRpY2tlciIpKQ0KDQpjb25zdF9kYXRhIDwtIGhvbGRpbmdzX2RmICU+JQ0KICBpbm5lcl9qb2luKHZvbF9kYXRhLCBieSA9IGMoImNvbnN0aXR1ZW50X3RpY2tlciIgPSAidGlja2VyIikpIA0KDQojIFB1dHRpbmcgZGF0YSBpbnRvIGxpc3Qgd2hpY2ggaXMgaWQnZCBieSB0aGVpciBwYXJlbnQgZXRmDQpsaXN0ZWRfZGF0YSA9IHNwbGl0KGNvbnN0X2RhdGEsIGYgPSBjb25zdF9kYXRhJGNvbXBvc2l0ZV90aWNrZXIpDQoNCmBgYA0KDQpjb25zdF9kYXRhOiBtZXJnZWQgZGF0YWZyYW1lIG9mIGNvbnN0aXR1ZW50IHRpY2tlcnMgYW5kIGl0J3MgY29ycmVzcG9uZGluZyBpdiArIHdlaWdodCB2YWx1ZXMNCg0KYGBge3J9DQpjb25zdF9kYXRhDQpgYGANCg0KIyBpbXBsaWVkIGNvcnJlbGF0aW9uIHN1bW1hcmllcw0KDQptYXBwZWQgaGVwZWQgZnVuY3Rpb24gaW1wX2NwcnJfdGFibCB0byB0aGUgbGlzdGVkX2RhdGEuIHRoZW4gaW5uZXJqb2luZWQgdGhlIHJlc3VsdGluZyBkYXRhZnJhbWUgd2l0aCBhIG5ldyBjb2x1bW4gY2FsbGVkICJzdW1fd2VpZ2h0cyIsIHRoaXMgdGVsbHMgdXMgdGhlIHN1bSB3ZWlnaHRzIG9mIHRoZSBob2xkaW5ncyB0aGF0IGkgaGFkIHZvbCBkYXRhIGZvciAoYW5kIGFwcGxpZWQgdGhlIGltcCBjb3JyZWxhdGlvbiB3aXRoKQ0KDQpgYGB7cn0NCiMgSW1wbGllZCBDb3JyZWxhdGlvbiBDYWxjdWxhdGlvbg0KDQp2YWx1ZXMgPSBtYXBfZGYobGlzdGVkX2RhdGEsIGZ1bmN0aW9uKHgpIGltcF9jb3JyX3RibCh4ID0geCwgY29tcF9kYXRhID0gY29tcF9kYXRhKSkNCg0KDQojIEV4dHJhOiBBZGRpbmcgc3VtbWVkX3dlaWdodHMgYXMgYSBjb2x1bW4gdG8gdGhlIGZpbmFsIGNhbGN1bGF0aW9ucw0KDQpzdW1tZWRfd2VpZ2h0cyA8LSBob2xkaW5nc19yYXcgJT4lDQogIGdyb3VwX2J5KGNvbXBvc2l0ZV90aWNrZXIpICU+JQ0KICBzdW1tYXJpc2Uoc3VtX3dlaWdodHMgPSBzdW0od2VpZ2h0KSkgJT4lDQogIGZpbHRlcihzdW1fd2VpZ2h0cyA+PSAwLjUwKSANCg0KZGYgPSB2YWx1ZXMgJT4lDQogIGlubmVyX2pvaW4oc3VtbWVkX3dlaWdodHMsIGJ5ID0gYygidGlja2VyIiA9ICJjb21wb3NpdGVfdGlja2VyIikpICU+JQ0KICBpbm5lcl9qb2luKHF1YW5kbF9kYXRhWyxjKCJ0aWNrZXIiLCAicHhhdG1pdiIpXSwgYnkgPSBjKCJ0aWNrZXIiKSkNCg0KYGBgDQoNCmBgYHtyfQ0KZGYNCmBgYA0KDQojIG1lcmdpbmcgZGF0YQ0KDQpzb3VyY2luZyBjb21wb3NpdGUgKGV0ZikgbmFtZSwgbWVyZ2luZyBhbGwgZGF0YWZyYW1lcw0KDQpgYGB7cn0NCg0KbWV0cmljcyA8LSB5YWhvb1FGKGMoIk5hbWUiLCAiQ2hhbmdlIGluIFBlcmNlbnQiKSkNCg0KdGlja2Vyc195ZiA9IGdldFF1b3RlKGRmJHRpY2tlciwgd2hhdCA9IG1ldHJpY3MpICU+JQ0KICBtdXRhdGUodGlja2VyID0gcm93bmFtZXMoLiksDQogICAgICAgICBOYW1lID0gaWZlbHNlKGlzLm5hKE5hbWUpLCB0aWNrZXIsIE5hbWUpKQ0KDQp0aWNrZXJfbmFtZXMgPC0gdGlja2Vyc195ZiAlPiUNCiAgbXV0YXRlKHRpY2tlciA9IHJvd25hbWVzKHRpY2tlcnNfeWYpKSAlPiUNCiAgc2VsZWN0KHRpY2tlciwgZXZlcnl0aGluZygpKQ0KDQpmaW5hbCA9IGRmICU+JQ0KICBsZWZ0X2pvaW4odGlja2VyX25hbWVzWyxjKCJ0aWNrZXIiLCAiTmFtZSIpXSwgYnkgPSBjKCJ0aWNrZXIiKSkgJT4lDQogIG11dGF0ZShkYXRlID0gIjIwMjEtMTEtMDEiKSAlPiUNCiAgc2VsZWN0KHRpY2tlciwgTmFtZSwgZGF0ZSwgcHhhdG1pdiwgZXZlcnl0aGluZygpKSAlPiUNCiAgcmVuYW1lKCJQcmljZSIgPSBweGF0bWl2KQ0KDQpgYGANCg0KZmluYWwNCg0KYGBge3J9DQpmaW5hbA0KYGBgDQoNCiMgY2xlYW5pbmcgZGYNCg0KYWRkZWQgJ2xldmVyYWdlZCcgY29sdW1uLCBzdHJpbmcgZGV0ZWN0ZWQgZm9yICIyIiwgIjMiLCAidWx0cmEiLCBvciAiZGlyZXhpb24iIGFuZCBsYWJlbGVkIHRoZW0gVFJVRSB0byBmaWx0ZXIgb3V0IGxldmVyYWdlZCBldGZzLg0KY2hhbmdlZCBhbGwgY29sdW1uIG5hbWUgdG8gbG93ZXItY2FzZSwgYW5kIHJlbmFtZWQgdGhlIGltcCBjb3JyIGNvbHVtbiB2YWx1ZXMgdG8gdGhlaXIgY29ycmVzcG9uZGluZyBpdm9sW2R0ZV0NCg0KYGBge3J9DQpmaW5hbCAgPSBmaW5hbCAlPiUNCiAgbXV0YXRlKA0KICAgIGxldmVyYWdlZCA9IHN0cl9kZXRlY3QoZmluYWwkTmFtZSwgIjN8MnxVbHRyYXxEaXJleGlvbiIpIA0KICApIA0KDQpjb2xuYW1lcyhmaW5hbCkgPC0gdG9sb3dlcihjb2xuYW1lcyhmaW5hbCkpDQoNCmZpbmFsID0gZmluYWwgJT4lDQogIG11dGF0ZV9pZihpcy5udW1lcmljLCByb3VuZCwgMykgJT4lDQogIHJlbmFtZSgNCiAgICAiZHRlMTAiID0gaXYxMGQsDQogICAgImR0ZTIwIiA9IGl2MjBkLA0KICAgICJkdGUzMCIgPSBpdjMwZCwNCiAgICAiZHRlNjAiID0gaXY2MGQsDQogICAgImR0ZTkwIiA9IGl2OTBkLA0KICAgICJkdGU2bSIgPSBpdjZtLA0KICAgICJkdGUxeXIiID0gaXYxeXINCiAgKQ0KYGBgDQoNCmBgYHtyfQ0KZmluYWwNCmBgYA0KDQojIGltcCBjb3JyIGVxdWF0aW9uIHVzZWQNCg0KeCA9IGNvbnN0aXR1ZW50IGRhdGEuDQpjb21wX2RhdGEgPSBjb21wb3NpdGUgKGV0ZikgZGF0YS4NCmZhY3RvciA9IGl2b2xbZHRlXSB1c2VkLg0KDQphZGpfd2VpZ2h0ID0gY29uc3RpdHVlbnQgd2VpZ2h0IGRpdmlkZWQgYnkgdGhlIHN1bSBvZiBhbGwgY29uc3RpdHVlbnQgd2VpZ2h0cyAoc29tZSBldGZzIHZhcnkgaW4gdGhlIGFtb3VudCBvZiB3ZWlnaHQgZGF0YSBhdmFpbGFibGUuIHNvIHRoaXMgaXMgdG8gYWRqdXN0ZWQgaXQgc28gdGhhdCBhbiBpbml0aWFsIHN1bSBvZiA3MCUgd2VpZ2h0ID0gMTAwJSB3ZWlnaHQpDQoNCnRpY2tlciA9IGNvbXBvc2l0ZSAoZXRmKSB0aWNrZXJzDQoNCmV0Zl9kYXRhID0gY29tcG9zaXRlIChldGYpIHZvbCBkYXRhDQoNCmV0Zl92YXIgPSBldGYgdmFyaWFuY2UgZm9yIHNlbGVjdGVkIGZhY3RvciAoaWUuIFNQWSBpdltkdGVdXjIpDQoNCnZhbHVlID0gaW1wIGNvcnIgZXF1YXRpb24NCiFbQ2FwdGlvbiBmb3IgdGhlIHBpY3R1cmUuXShpbXBfY29ycl9pbWFnZS5wbmcpDQoNCmV0ZiB2YXJpYW5jZS9zdW0oY29uc3RpdHVlbnQgdm9sICogY29uc3RpdHVlbnQgYWRqdXN0ZWRfd2VpZ2h0KV4yDQoNCmBgYHtyfQ0KaW1wX2NvcnJfdmFsdWUgPC0gZnVuY3Rpb24oeCA9IHgsIGNvbXBfZGF0YSA9IGNvbXBfZGF0YSwgZmFjdG9yID0gIml2MTBkIil7DQogIA0KICBhZGpfd2VpZ2h0ID0geCR3ZWlnaHQvc3VtKHgkd2VpZ2h0KQ0KICB0aWNrZXIgPSB1bmlxdWUoeCRjb21wb3NpdGVfdGlja2VyKQ0KICANCiAgZXRmX2RhdGEgPSBmaWx0ZXIoY29tcF9kYXRhLCBjb21wb3NpdGVfdGlja2VyID09IHRpY2tlcikNCiAgZXRmX3ZhciA9IGV0Zl9kYXRhWyxmYWN0b3JdXjINCiAgDQogIHZhbHVlID0gZXRmX3Zhci8oc3VtKHhbLGZhY3Rvcl0qYWRqX3dlaWdodCleMikNCiAgcmV0dXJuKHZhbHVlKQ0KfQ0KYGBgDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0K