Problem Set 1 R Computing Section

Published Document can be accessed on https://rpubs.com/TCML/HW1

Name : Chun Ming LAM, EID : 58555905

library('quantmod')
library('e1071')
library('tidyverse')


stock_list = c("AMZN","ADBE","JNJ")
from.dat = "2019-01-02"
to.dat = "2024-09-20"

5. a,b) Using quantmod package to get stock adjusted prices, for simple and log returns

Collecting data from 2019-01-02 to 2024-09-20 for Amazon, Adobe and Johnson & Johnson, plotting simple and log returns time series


get_basics <-function(stock_list, from = NULL, to = NULL, type = "Ad", print_graphs = TRUE){
  # function that gets the price, simple & log returns given a list of stock tickers, type changes the type of stock price data retrieved {"Ad","Hi","Lo","Cl"}
  
  # getting symbols
  
  args <- list(Symbols = stock_list, auto.assign = TRUE)
  
  # optional params
  if (!is.null(from)){
    args$from <- from
  }
  if (!is.null(to)){
    args$to <- to
  }
  
  out = do.call("getSymbols",args)
  sval_df = xts()
  sret_df = xts()
  lret_df = xts()

  for (stock in stock_list) {
    # getting data 
    sval = do.call(type,list(get(stock)))
    
    # caculating returns
    sret = (sval - lag(sval))/lag(sval)
    lret = log( sval/ lag(sval))
    
    sval_df = merge(sval_df,sval)
    sret_df = merge(sret_df,sret)
    lret_df = merge(lret_df,lret)
    
   
    if (print_graphs){
       # print simple & log returns
      par(mfrow = c(1,2),mar= c(1,1,1,1))
      print(plot(sret, main = "Simple Returns"))
      print(plot(lret, main = "log Returns"))
      
      mtext(stock,line = -1, outer = TRUE)
    }
  }
  
  return(list(sval_df=sval_df,sret_df=sret_df,lret_df=lret_df))
}
 

# get stock values
out = get_basics(stock_list, from = from.dat, to = to.dat)

list2env(out,envir = .GlobalEnv)
<environment: R_GlobalEnv>

5. c,d) Compute Sample Mean, Variance, Skewness, and Kurtosis

Comparing Sample Mean: in general log return mean is less than simple return. Amazon & Adobe has a much higher mean return than Johnson & Johnson


print("Simple Returns Mean")
[1] "Simple Returns Mean"
print(apply(sret_df,2, mean,na.rm = TRUE))
AMZN.Adjusted ADBE.Adjusted  JNJ.Adjusted 
 0.0008626735  0.0008632000  0.0003619072 
print("Log Returns Mean")
[1] "Log Returns Mean"
print(apply(lret_df,2, mean,na.rm = TRUE))
AMZN.Adjusted ADBE.Adjusted  JNJ.Adjusted 
 0.0006280246  0.0005924547  0.0002874095 

Comparing Sample Variance: a visual inspection of the data show that they are very similar. Here Amazon & Adobe also shows a much higher variance compared to Johnson & Johnson. This is somewhat in line with expectation for efficient markets, as we expect higher returns to compensate for higher level of risk (variance)

print("Simple Returns Variance")
[1] "Simple Returns Variance"
print(apply(sret_df,2, var,na.rm = TRUE))
AMZN.Adjusted ADBE.Adjusted  JNJ.Adjusted 
 0.0004693387  0.0005377012  0.0001492434 
print("Log Returns Variance")
[1] "Log Returns Variance"
print(apply(lret_df,2, var,na.rm = TRUE))
AMZN.Adjusted ADBE.Adjusted  JNJ.Adjusted 
 0.0004692713  0.0005438017  0.0001489195 

Comparing Sample Skewness: log skewness is in general less/ more negative compared to simple skewness. We expect the simple returns to be right skewed in general whilst log return to be normal, therefore this is inline with our expectation. Furthermore, Johnson & Johnson is also positively skewed, which is preferable to investors and not captured directly in Sharpe Ratios

print("Simple Returns Skew")
[1] "Simple Returns Skew"
print(apply(sret_df,2, skewness, na.rm = TRUE))
AMZN.Adjusted ADBE.Adjusted  JNJ.Adjusted 
    0.0677942    -0.3195508     0.2392024 
print("Log Returns Skew")
[1] "Log Returns Skew"
print(apply(lret_df,2, skewness, na.rm = TRUE))
AMZN.Adjusted ADBE.Adjusted  JNJ.Adjusted 
  -0.13936858   -0.68562825    0.05538403 

Comparing Sample Excess Kurtosis: log excess kurtosis is in general more than than simple excess kurtosis. both Adobe and Johnson & Johnson has a higher level of Kurtosis compared to Amazon, suggesting that returns more distributed (fatter tails) compared to the normal distribution, this correspond to additional risks that is not captured by Sharpe Ratios

print("Simple Returns Excess Kurtosis")
[1] "Simple Returns Excess Kurtosis"
print(apply(sret_df,2, kurtosis, na.rm = TRUE)-3)
AMZN.Adjusted ADBE.Adjusted  JNJ.Adjusted 
     1.347341      5.534483      5.160415 
print("Log Returns Excess Kurtosis")
[1] "Log Returns Excess Kurtosis"
print(apply(lret_df,2, kurtosis, na.rm = TRUE)-3)
AMZN.Adjusted ADBE.Adjusted  JNJ.Adjusted 
     1.445325      6.041365      5.011255 

5. e,f,g) Pairwise Correlation & Summary

Pairwise Correlation of the simple returns of stocks shows that, Amazon is highly correlated to Adobe. In general log returns shows lower correlations, although the relative magnitudes are similar.

given that Adobe has negative skew and high excess kurtosis, I believe ti is more likely to encounter big draw downs compared to the other

similarly given that Johnson & Johnson has a high positive skew, and relatively lower variance, I think it’s more likely to produce higher returns compared to losses

print('Stock Price Correlation')
[1] "Stock Price Correlation"
print(cor(sval_df, use = 'complete.obs'))
              AMZN.Adjusted ADBE.Adjusted JNJ.Adjusted
AMZN.Adjusted     1.0000000     0.8641662    0.4627563
ADBE.Adjusted     0.8641662     1.0000000    0.5556758
JNJ.Adjusted      0.4627563     0.5556758    1.0000000
print('Simple Return Correlation')
[1] "Simple Return Correlation"
print(cor(sret_df, use = 'complete.obs'))
              AMZN.Adjusted ADBE.Adjusted JNJ.Adjusted
AMZN.Adjusted     1.0000000     0.6134342    0.1978098
ADBE.Adjusted     0.6134342     1.0000000    0.2598150
JNJ.Adjusted      0.1978098     0.2598150    1.0000000

5. h) Histogram and Density Plots


hist_dest_plot <- function (df){
  # takes a xts object, and plot the histogram and density of all it's entries in a column wise manner
  
  for (col in colnames(df)){
    
    # data to be used 
    dat = df[,col]
    
    # finding y range 
    den <- density(dat, na.rm = TRUE)
    ylim_val = c(0, max(den$y)*1.1)
    
    # plot histogram
    hist(df[,col],30,probability = TRUE, main = col, xlab = "Returns", ylim = ylim_val)
    
    # plot density line
    par(lwd = 2, col = 'red')
    lines(den)
    
    # plot normal distribution
    
    x_values <- seq(min(dat,na.rm= TRUE), max(dat,na.rm= TRUE), length = 100) 
    normal_dist <- dnorm(x_values, mean = mean(dat,na.rm= TRUE), sd = sd(dat,na.rm= TRUE))
    
    par(lwd = 2, col = 'blue')
    lines(x_values, normal_dist)
    
    par(col = 'black')
    legend("topright", legend = c("Red = Density", "Blue = Normal Distribution"))

  
  }
}
# plotting the simple returns
hist_dest_plot(sret_df)

# plotting the log returns
hist_dest_plot(lret_df)

5. i) Hypothesis Testing

We let the null hypothesis \(H_0 : \mu = 0\) and the alternate hypothesis \(H_1 : \mu \neq 0\) , we conduct a two tailed \(t\) test to see if the sample mean t statistic is inside or outside the confidence interval

mass_t <- function(df){
  for (col in colnames(df)){
    dat = df[,col]
    
    print(paste("data: ", col))
    print(t.test(as.vector(dat),na.rm = TRUE))
  }
}

mass_t(sret_df)
[1] "data:  AMZN.Adjusted"

    One Sample t-test

data:  as.vector(dat)
t = 1.51, df = 1437, p-value = 0.1313
alternative hypothesis: true mean is not equal to 0
95 percent confidence interval:
 -0.0002579967  0.0019833436
sample estimates:
   mean of x 
0.0008626735 

[1] "data:  ADBE.Adjusted"

    One Sample t-test

data:  as.vector(dat)
t = 1.4116, df = 1437, p-value = 0.1583
alternative hypothesis: true mean is not equal to 0
95 percent confidence interval:
 -0.0003363134  0.0020627134
sample estimates:
mean of x 
0.0008632 

[1] "data:  JNJ.Adjusted"

    One Sample t-test

data:  as.vector(dat)
t = 1.1234, df = 1437, p-value = 0.2615
alternative hypothesis: true mean is not equal to 0
95 percent confidence interval:
 -0.0002700420  0.0009938563
sample estimates:
   mean of x 
0.0003619072 

the result shows that for all 3 stocks, we cannot reject the null hypothesis at a \(5\%\) level, as the p value of the t statistic shows that they has \(13.1\%, 15.8\%\) and \(26.2\%\) chance respectively for their true mean to be equal to 0, given our sample.

6. a) Plotting 3 month T bill log return


getSymbols("DGS3MO", src = "FRED", from = from.dat, to=to.dat)
[1] "DGS3MO"
# convert to daily data, assume 252 trading day in a year
DGS3MO = DGS3MO/100/252

log_DGS3MO = log(1+DGS3MO)
plot(log_DGS3MO, main = "Log Daily Returns of 3 Month T Bills")


# get closing prices
out = get_basics(stock_list,from = from.dat , to = to.dat, type = "Cl", print_graphs = FALSE)
cl_lret_df = out$lret_df

6. b,c) Calculating and Interpreting Sharpe Ratio

Using the formula

\[ S_i = \frac{E_t\{ R_{i,t} - R_{f,t}\}}{Var_t\{R_{i,t}\}} \]


# formatting data
cols = colnames(cl_lret_df)

sharpe_data = merge(cl_lret_df,DGS3MO)

equity = sharpe_data[,cols]
DGS3MO = sharpe_data[,"DGS3MO"]

Rf = equity
Rf[,] = DGS3MO

# calculating Sharpe
s_mean <- equity %>% -Rf %>% apply(2,mean,na.rm=TRUE)
s_var <- equity %>% apply(2,var,na.rm=TRUE)

sharpe <- s_mean/ s_var
print(sharpe)
AMZN.Close ADBE.Close  JNJ.Close 
 1.0012637  0.8479906  0.7623467 

results shows that Amazon has the highest Sharpe Ratio, although the plot for the cumulative returns since “inception” shows that Amazon and Adobe also suffers from massive draw downs in this period. this reflects our discussion when comparing the stock’s skewness and kurtosis in 5. c)

cumret <- sret_df %>% merge(DGS3MO) %>% na.fill(0) %>% +1 %>% cumprod()

# plotting cumlative returns for the stocks & T Bills
plot(cumret)
legend("topleft", legend = c(cols,"Rf"))

red : AMZN, black ADBE, green: JNJ, blue: 3 Month T Bill

LS0tDQp0aXRsZTogIlIgTm90ZWJvb2siDQpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sNCi0tLQ0KDQojIyBQcm9ibGVtIFNldCAxIFIgQ29tcHV0aW5nIFNlY3Rpb24NCg0KKioqUHVibGlzaGVkIERvY3VtZW50IGNhbiBiZSBhY2Nlc3NlZCBvbiA8aHR0cHM6Ly9ycHVicy5jb20vVENNTC9IVzE+KioqDQoNCk5hbWUgOiBDaHVuIE1pbmcgTEFNLCBFSUQgOiA1ODU1NTkwNQ0KDQpgYGB7cn0NCmxpYnJhcnkoJ3F1YW50bW9kJykNCmxpYnJhcnkoJ2UxMDcxJykNCmxpYnJhcnkoJ3RpZHl2ZXJzZScpDQoNCg0Kc3RvY2tfbGlzdCA9IGMoIkFNWk4iLCJBREJFIiwiSk5KIikNCmZyb20uZGF0ID0gIjIwMTktMDEtMDIiDQp0by5kYXQgPSAiMjAyNC0wOS0yMCINCg0KDQpgYGANCg0KIyMjIDUuIGEsYikgVXNpbmcgcXVhbnRtb2QgcGFja2FnZSB0byBnZXQgc3RvY2sgYWRqdXN0ZWQgcHJpY2VzLCBmb3Igc2ltcGxlIGFuZCBsb2cgcmV0dXJucw0KDQpDb2xsZWN0aW5nIGRhdGEgZnJvbSAyMDE5LTAxLTAyIHRvIDIwMjQtMDktMjAgZm9yIEFtYXpvbiwgQWRvYmUgYW5kIEpvaG5zb24gJiBKb2huc29uLCBwbG90dGluZyBzaW1wbGUgYW5kIGxvZyByZXR1cm5zIHRpbWUgc2VyaWVzDQoNCmBgYHtyfQ0KDQpnZXRfYmFzaWNzIDwtZnVuY3Rpb24oc3RvY2tfbGlzdCwgZnJvbSA9IE5VTEwsIHRvID0gTlVMTCwgdHlwZSA9ICJBZCIsIHByaW50X2dyYXBocyA9IFRSVUUpew0KICAjIGZ1bmN0aW9uIHRoYXQgZ2V0cyB0aGUgcHJpY2UsIHNpbXBsZSAmIGxvZyByZXR1cm5zIGdpdmVuIGEgbGlzdCBvZiBzdG9jayB0aWNrZXJzLCB0eXBlIGNoYW5nZXMgdGhlIHR5cGUgb2Ygc3RvY2sgcHJpY2UgZGF0YSByZXRyaWV2ZWQgeyJBZCIsIkhpIiwiTG8iLCJDbCJ9DQogIA0KICAjIGdldHRpbmcgc3ltYm9scw0KICANCiAgYXJncyA8LSBsaXN0KFN5bWJvbHMgPSBzdG9ja19saXN0LCBhdXRvLmFzc2lnbiA9IFRSVUUpDQogIA0KICAjIG9wdGlvbmFsIHBhcmFtcw0KICBpZiAoIWlzLm51bGwoZnJvbSkpew0KICAgIGFyZ3MkZnJvbSA8LSBmcm9tDQogIH0NCiAgaWYgKCFpcy5udWxsKHRvKSl7DQogICAgYXJncyR0byA8LSB0bw0KICB9DQogIA0KICBvdXQgPSBkby5jYWxsKCJnZXRTeW1ib2xzIixhcmdzKQ0KICBzdmFsX2RmID0geHRzKCkNCiAgc3JldF9kZiA9IHh0cygpDQogIGxyZXRfZGYgPSB4dHMoKQ0KDQogIGZvciAoc3RvY2sgaW4gc3RvY2tfbGlzdCkgew0KICAgICMgZ2V0dGluZyBkYXRhIA0KICAgIHN2YWwgPSBkby5jYWxsKHR5cGUsbGlzdChnZXQoc3RvY2spKSkNCiAgICANCiAgICAjIGNhY3VsYXRpbmcgcmV0dXJucw0KICAgIHNyZXQgPSAoc3ZhbCAtIGxhZyhzdmFsKSkvbGFnKHN2YWwpDQogICAgbHJldCA9IGxvZyggc3ZhbC8gbGFnKHN2YWwpKQ0KICAgIA0KICAgIHN2YWxfZGYgPSBtZXJnZShzdmFsX2RmLHN2YWwpDQogICAgc3JldF9kZiA9IG1lcmdlKHNyZXRfZGYsc3JldCkNCiAgICBscmV0X2RmID0gbWVyZ2UobHJldF9kZixscmV0KQ0KICAgIA0KICAgDQogICAgaWYgKHByaW50X2dyYXBocyl7DQogICAgICAgIyBwcmludCBzaW1wbGUgJiBsb2cgcmV0dXJucw0KICAgICAgcGFyKG1mcm93ID0gYygxLDIpLG1hcj0gYygxLDEsMSwxKSkNCiAgICAgIHByaW50KHBsb3Qoc3JldCwgbWFpbiA9ICJTaW1wbGUgUmV0dXJucyIpKQ0KICAgICAgcHJpbnQocGxvdChscmV0LCBtYWluID0gImxvZyBSZXR1cm5zIikpDQogICAgICANCiAgICAgIG10ZXh0KHN0b2NrLGxpbmUgPSAtMSwgb3V0ZXIgPSBUUlVFKQ0KICAgIH0NCiAgfQ0KICANCiAgcmV0dXJuKGxpc3Qoc3ZhbF9kZj1zdmFsX2RmLHNyZXRfZGY9c3JldF9kZixscmV0X2RmPWxyZXRfZGYpKQ0KfQ0KIA0KDQojIGdldCBzdG9jayB2YWx1ZXMNCm91dCA9IGdldF9iYXNpY3Moc3RvY2tfbGlzdCwgZnJvbSA9IGZyb20uZGF0LCB0byA9IHRvLmRhdCkNCmxpc3QyZW52KG91dCxlbnZpciA9IC5HbG9iYWxFbnYpDQoNCmBgYA0KDQojIyMgNS4gYyxkKSBDb21wdXRlIFNhbXBsZSBNZWFuLCBWYXJpYW5jZSwgU2tld25lc3MsIGFuZCBLdXJ0b3Npcw0KDQoqKkNvbXBhcmluZyBTYW1wbGUgTWVhbjoqKiBpbiBnZW5lcmFsIGxvZyByZXR1cm4gbWVhbiBpcyBsZXNzIHRoYW4gc2ltcGxlIHJldHVybi4gQW1hem9uICYgQWRvYmUgaGFzIGEgbXVjaCBoaWdoZXIgbWVhbiByZXR1cm4gdGhhbiBKb2huc29uICYgSm9obnNvbg0KDQpgYGB7cn0NCg0KcHJpbnQoIlNpbXBsZSBSZXR1cm5zIE1lYW4iKQ0KcHJpbnQoYXBwbHkoc3JldF9kZiwyLCBtZWFuLG5hLnJtID0gVFJVRSkpDQoNCnByaW50KCJMb2cgUmV0dXJucyBNZWFuIikNCnByaW50KGFwcGx5KGxyZXRfZGYsMiwgbWVhbixuYS5ybSA9IFRSVUUpKQ0KDQpgYGANCg0KKipDb21wYXJpbmcgU2FtcGxlIFZhcmlhbmNlOioqIGEgdmlzdWFsIGluc3BlY3Rpb24gb2YgdGhlIGRhdGEgc2hvdyB0aGF0IHRoZXkgYXJlIHZlcnkgc2ltaWxhci4gSGVyZSBBbWF6b24gJiBBZG9iZSBhbHNvIHNob3dzIGEgbXVjaCBoaWdoZXIgdmFyaWFuY2UgY29tcGFyZWQgdG8gSm9obnNvbiAmIEpvaG5zb24uIFRoaXMgaXMgc29tZXdoYXQgaW4gbGluZSB3aXRoIGV4cGVjdGF0aW9uIGZvciBlZmZpY2llbnQgbWFya2V0cywgYXMgd2UgZXhwZWN0IGhpZ2hlciByZXR1cm5zIHRvIGNvbXBlbnNhdGUgZm9yIGhpZ2hlciBsZXZlbCBvZiByaXNrICh2YXJpYW5jZSkNCg0KYGBge3J9DQpwcmludCgiU2ltcGxlIFJldHVybnMgVmFyaWFuY2UiKQ0KcHJpbnQoYXBwbHkoc3JldF9kZiwyLCB2YXIsbmEucm0gPSBUUlVFKSkNCg0KcHJpbnQoIkxvZyBSZXR1cm5zIFZhcmlhbmNlIikNCnByaW50KGFwcGx5KGxyZXRfZGYsMiwgdmFyLG5hLnJtID0gVFJVRSkpDQoNCmBgYA0KDQoqKkNvbXBhcmluZyBTYW1wbGUgU2tld25lc3M6KiogbG9nIHNrZXduZXNzIGlzIGluIGdlbmVyYWwgbGVzcy8gbW9yZSBuZWdhdGl2ZSBjb21wYXJlZCB0byBzaW1wbGUgc2tld25lc3MuIFdlIGV4cGVjdCB0aGUgc2ltcGxlIHJldHVybnMgdG8gYmUgcmlnaHQgc2tld2VkIGluIGdlbmVyYWwgd2hpbHN0IGxvZyByZXR1cm4gdG8gYmUgbm9ybWFsLCB0aGVyZWZvcmUgdGhpcyBpcyBpbmxpbmUgd2l0aCBvdXIgZXhwZWN0YXRpb24uIEZ1cnRoZXJtb3JlLCBKb2huc29uICYgSm9obnNvbiBpcyBhbHNvIHBvc2l0aXZlbHkgc2tld2VkLCB3aGljaCBpcyBwcmVmZXJhYmxlIHRvIGludmVzdG9ycyBhbmQgbm90IGNhcHR1cmVkIGRpcmVjdGx5IGluIFNoYXJwZSBSYXRpb3MNCg0KYGBge3J9DQpwcmludCgiU2ltcGxlIFJldHVybnMgU2tldyIpDQpwcmludChhcHBseShzcmV0X2RmLDIsIHNrZXduZXNzLCBuYS5ybSA9IFRSVUUpKQ0KDQpwcmludCgiTG9nIFJldHVybnMgU2tldyIpDQpwcmludChhcHBseShscmV0X2RmLDIsIHNrZXduZXNzLCBuYS5ybSA9IFRSVUUpKQ0KYGBgDQoNCioqQ29tcGFyaW5nIFNhbXBsZSBFeGNlc3MgS3VydG9zaXM6KiogbG9nIGV4Y2VzcyBrdXJ0b3NpcyBpcyBpbiBnZW5lcmFsIG1vcmUgdGhhbiB0aGFuIHNpbXBsZSBleGNlc3Mga3VydG9zaXMuIGJvdGggQWRvYmUgYW5kIEpvaG5zb24gJiBKb2huc29uIGhhcyBhIGhpZ2hlciBsZXZlbCBvZiBLdXJ0b3NpcyBjb21wYXJlZCB0byBBbWF6b24sIHN1Z2dlc3RpbmcgdGhhdCByZXR1cm5zIG1vcmUgZGlzdHJpYnV0ZWQgKGZhdHRlciB0YWlscykgY29tcGFyZWQgdG8gdGhlIG5vcm1hbCBkaXN0cmlidXRpb24sIHRoaXMgY29ycmVzcG9uZCB0byBhZGRpdGlvbmFsIHJpc2tzIHRoYXQgaXMgbm90IGNhcHR1cmVkIGJ5IFNoYXJwZSBSYXRpb3MNCg0KYGBge3J9DQpwcmludCgiU2ltcGxlIFJldHVybnMgRXhjZXNzIEt1cnRvc2lzIikNCnByaW50KGFwcGx5KHNyZXRfZGYsMiwga3VydG9zaXMsIG5hLnJtID0gVFJVRSktMykNCg0KcHJpbnQoIkxvZyBSZXR1cm5zIEV4Y2VzcyBLdXJ0b3NpcyIpDQpwcmludChhcHBseShscmV0X2RmLDIsIGt1cnRvc2lzLCBuYS5ybSA9IFRSVUUpLTMpDQpgYGANCg0KIyMjIDUuIGUsZixnKSBQYWlyd2lzZSBDb3JyZWxhdGlvbiAmIFN1bW1hcnkNCg0KUGFpcndpc2UgQ29ycmVsYXRpb24gb2YgdGhlIHNpbXBsZSByZXR1cm5zIG9mIHN0b2NrcyBzaG93cyB0aGF0LCBBbWF6b24gaXMgaGlnaGx5IGNvcnJlbGF0ZWQgdG8gQWRvYmUuIEluIGdlbmVyYWwgbG9nIHJldHVybnMgc2hvd3MgbG93ZXIgY29ycmVsYXRpb25zLCBhbHRob3VnaCB0aGUgcmVsYXRpdmUgbWFnbml0dWRlcyBhcmUgc2ltaWxhci4NCg0KZ2l2ZW4gdGhhdCBBZG9iZSBoYXMgbmVnYXRpdmUgc2tldyBhbmQgaGlnaCBleGNlc3Mga3VydG9zaXMsIEkgYmVsaWV2ZSB0aSBpcyBtb3JlIGxpa2VseSB0byBlbmNvdW50ZXIgYmlnIGRyYXcgZG93bnMgY29tcGFyZWQgdG8gdGhlIG90aGVyDQoNCnNpbWlsYXJseSBnaXZlbiB0aGF0IEpvaG5zb24gJiBKb2huc29uIGhhcyBhIGhpZ2ggcG9zaXRpdmUgc2tldywgYW5kIHJlbGF0aXZlbHkgbG93ZXIgdmFyaWFuY2UsIEkgdGhpbmsgaXQncyBtb3JlIGxpa2VseSB0byBwcm9kdWNlIGhpZ2hlciByZXR1cm5zIGNvbXBhcmVkIHRvIGxvc3Nlcw0KDQpgYGB7cn0NCnByaW50KCdTdG9jayBQcmljZSBDb3JyZWxhdGlvbicpDQpwcmludChjb3Ioc3ZhbF9kZiwgdXNlID0gJ2NvbXBsZXRlLm9icycpKQ0KDQpwcmludCgnU2ltcGxlIFJldHVybiBDb3JyZWxhdGlvbicpDQpwcmludChjb3Ioc3JldF9kZiwgdXNlID0gJ2NvbXBsZXRlLm9icycpKQ0KYGBgDQoNCiMjIyA1LiBoKSBIaXN0b2dyYW0gYW5kIERlbnNpdHkgUGxvdHMNCg0KYGBge3J9DQoNCmhpc3RfZGVzdF9wbG90IDwtIGZ1bmN0aW9uIChkZil7DQogICMgdGFrZXMgYSB4dHMgb2JqZWN0LCBhbmQgcGxvdCB0aGUgaGlzdG9ncmFtIGFuZCBkZW5zaXR5IG9mIGFsbCBpdCdzIGVudHJpZXMgaW4gYSBjb2x1bW4gd2lzZSBtYW5uZXINCiAgDQogIGZvciAoY29sIGluIGNvbG5hbWVzKGRmKSl7DQogICAgDQogICAgIyBkYXRhIHRvIGJlIHVzZWQgDQogICAgZGF0ID0gZGZbLGNvbF0NCiAgICANCiAgICAjIGZpbmRpbmcgeSByYW5nZSANCiAgICBkZW4gPC0gZGVuc2l0eShkYXQsIG5hLnJtID0gVFJVRSkNCiAgICB5bGltX3ZhbCA9IGMoMCwgbWF4KGRlbiR5KSoxLjEpDQogICAgDQogICAgIyBwbG90IGhpc3RvZ3JhbQ0KICAgIGhpc3QoZGZbLGNvbF0sMzAscHJvYmFiaWxpdHkgPSBUUlVFLCBtYWluID0gY29sLCB4bGFiID0gIlJldHVybnMiLCB5bGltID0geWxpbV92YWwpDQogICAgDQogICAgIyBwbG90IGRlbnNpdHkgbGluZQ0KICAgIHBhcihsd2QgPSAyLCBjb2wgPSAncmVkJykNCiAgICBsaW5lcyhkZW4pDQogICAgDQogICAgIyBwbG90IG5vcm1hbCBkaXN0cmlidXRpb24NCiAgICANCiAgICB4X3ZhbHVlcyA8LSBzZXEobWluKGRhdCxuYS5ybT0gVFJVRSksIG1heChkYXQsbmEucm09IFRSVUUpLCBsZW5ndGggPSAxMDApIA0KICAgIG5vcm1hbF9kaXN0IDwtIGRub3JtKHhfdmFsdWVzLCBtZWFuID0gbWVhbihkYXQsbmEucm09IFRSVUUpLCBzZCA9IHNkKGRhdCxuYS5ybT0gVFJVRSkpDQogICAgDQogICAgcGFyKGx3ZCA9IDIsIGNvbCA9ICdibHVlJykNCiAgICBsaW5lcyh4X3ZhbHVlcywgbm9ybWFsX2Rpc3QpDQogICAgDQogICAgcGFyKGNvbCA9ICdibGFjaycpDQogICAgbGVnZW5kKCJ0b3ByaWdodCIsIGxlZ2VuZCA9IGMoIlJlZCA9IERlbnNpdHkiLCAiQmx1ZSA9IE5vcm1hbCBEaXN0cmlidXRpb24iKSkNCg0KICANCiAgfQ0KfQ0KDQpgYGANCg0KYGBge3J9DQojIHBsb3R0aW5nIHRoZSBzaW1wbGUgcmV0dXJucw0KaGlzdF9kZXN0X3Bsb3Qoc3JldF9kZikNCmBgYA0KDQpgYGB7cn0NCiMgcGxvdHRpbmcgdGhlIGxvZyByZXR1cm5zDQpoaXN0X2Rlc3RfcGxvdChscmV0X2RmKQ0KYGBgDQoNCiMjIyA1LiBpKSBIeXBvdGhlc2lzIFRlc3RpbmcNCg0KV2UgbGV0IHRoZSBudWxsIGh5cG90aGVzaXMgJEhfMCA6IFxtdSA9IDAkIGFuZCB0aGUgYWx0ZXJuYXRlIGh5cG90aGVzaXMgJEhfMSA6IFxtdSBcbmVxIDAkICwgd2UgY29uZHVjdCBhIHR3byB0YWlsZWQgJHQkIHRlc3QgdG8gc2VlIGlmIHRoZSBzYW1wbGUgbWVhbiB0IHN0YXRpc3RpYyBpcyBpbnNpZGUgb3Igb3V0c2lkZSB0aGUgY29uZmlkZW5jZSBpbnRlcnZhbA0KDQpgYGB7cn0NCm1hc3NfdCA8LSBmdW5jdGlvbihkZil7DQogIGZvciAoY29sIGluIGNvbG5hbWVzKGRmKSl7DQogICAgZGF0ID0gZGZbLGNvbF0NCiAgICANCiAgICBwcmludChwYXN0ZSgiZGF0YTogIiwgY29sKSkNCiAgICBwcmludCh0LnRlc3QoYXMudmVjdG9yKGRhdCksbmEucm0gPSBUUlVFKSkNCiAgfQ0KfQ0KDQptYXNzX3Qoc3JldF9kZikNCmBgYA0KDQp0aGUgcmVzdWx0IHNob3dzIHRoYXQgZm9yIGFsbCAzIHN0b2Nrcywgd2UgY2Fubm90IHJlamVjdCB0aGUgbnVsbCBoeXBvdGhlc2lzIGF0IGEgJDVcJSQgbGV2ZWwsIGFzIHRoZSBwIHZhbHVlIG9mIHRoZSB0IHN0YXRpc3RpYyBzaG93cyB0aGF0IHRoZXkgaGFzICQxMy4xXCUsIDE1LjhcJSQgYW5kICQyNi4yXCUkIGNoYW5jZSByZXNwZWN0aXZlbHkgZm9yIHRoZWlyIHRydWUgbWVhbiB0byBiZSBlcXVhbCB0byAwLCBnaXZlbiBvdXIgc2FtcGxlLg0KDQojIyMgNi4gYSkgUGxvdHRpbmcgMyBtb250aCBUIGJpbGwgbG9nIHJldHVybg0KDQpgYGB7cn0NCg0KZ2V0U3ltYm9scygiREdTM01PIiwgc3JjID0gIkZSRUQiLCBmcm9tID0gZnJvbS5kYXQsIHRvPXRvLmRhdCkNCg0KIyBjb252ZXJ0IHRvIGRhaWx5IGRhdGEsIGFzc3VtZSAyNTIgdHJhZGluZyBkYXkgaW4gYSB5ZWFyDQpER1MzTU8gPSBER1MzTU8vMTAwLzI1Mg0KDQpsb2dfREdTM01PID0gbG9nKDErREdTM01PKQ0KcGxvdChsb2dfREdTM01PLCBtYWluID0gIkxvZyBEYWlseSBSZXR1cm5zIG9mIDMgTW9udGggVCBCaWxscyIpDQoNCiMgZ2V0IGNsb3NpbmcgcHJpY2VzDQpvdXQgPSBnZXRfYmFzaWNzKHN0b2NrX2xpc3QsZnJvbSA9IGZyb20uZGF0ICwgdG8gPSB0by5kYXQsIHR5cGUgPSAiQ2wiLCBwcmludF9ncmFwaHMgPSBGQUxTRSkNCmNsX2xyZXRfZGYgPSBvdXQkbHJldF9kZg0KDQoNCg0KYGBgDQoNCiMjIyA2LiBiLGMpIENhbGN1bGF0aW5nIGFuZCBJbnRlcnByZXRpbmcgU2hhcnBlIFJhdGlvDQoNClVzaW5nIHRoZSBmb3JtdWxhDQoNCiQkDQpTX2kgPSBcZnJhY3tFX3RceyBSX3tpLHR9IC0gUl97Zix0fVx9fXtWYXJfdFx7Ul97aSx0fVx9fQ0KJCQNCg0KYGBge3J9DQoNCiMgZm9ybWF0dGluZyBkYXRhDQpjb2xzID0gY29sbmFtZXMoY2xfbHJldF9kZikNCg0Kc2hhcnBlX2RhdGEgPSBtZXJnZShjbF9scmV0X2RmLERHUzNNTykNCg0KZXF1aXR5ID0gc2hhcnBlX2RhdGFbLGNvbHNdDQpER1MzTU8gPSBzaGFycGVfZGF0YVssIkRHUzNNTyJdDQoNClJmID0gZXF1aXR5DQpSZlssXSA9IERHUzNNTw0KDQojIGNhbGN1bGF0aW5nIFNoYXJwZQ0Kc19tZWFuIDwtIGVxdWl0eSAlPiUgLVJmICU+JSBhcHBseSgyLG1lYW4sbmEucm09VFJVRSkNCnNfdmFyIDwtIGVxdWl0eSAlPiUgYXBwbHkoMix2YXIsbmEucm09VFJVRSkNCg0Kc2hhcnBlIDwtIHNfbWVhbi8gc192YXINCnByaW50KHNoYXJwZSkNCmBgYA0KDQpyZXN1bHRzIHNob3dzIHRoYXQgQW1hem9uIGhhcyB0aGUgaGlnaGVzdCBTaGFycGUgUmF0aW8sIGFsdGhvdWdoIHRoZSBwbG90IGZvciB0aGUgY3VtdWxhdGl2ZSByZXR1cm5zIHNpbmNlICJpbmNlcHRpb24iIHNob3dzIHRoYXQgQW1hem9uIGFuZCBBZG9iZSBhbHNvIHN1ZmZlcnMgZnJvbSBtYXNzaXZlIGRyYXcgZG93bnMgaW4gdGhpcyBwZXJpb2QuIHRoaXMgcmVmbGVjdHMgb3VyIGRpc2N1c3Npb24gd2hlbiBjb21wYXJpbmcgdGhlIHN0b2NrJ3Mgc2tld25lc3MgYW5kIGt1cnRvc2lzIGluICoqNS4gYykqKg0KDQpgYGB7cn0NCmN1bXJldCA8LSBzcmV0X2RmICU+JSBtZXJnZShER1MzTU8pICU+JSBuYS5maWxsKDApICU+JSArMSAlPiUgY3VtcHJvZCgpDQoNCiMgcGxvdHRpbmcgY3VtbGF0aXZlIHJldHVybnMgZm9yIHRoZSBzdG9ja3MgJiBUIEJpbGxzDQpwbG90KGN1bXJldCkNCmxlZ2VuZCgidG9wbGVmdCIsIGxlZ2VuZCA9IGMoY29scywiUmYiKSkNCg0KYGBgDQoNCnJlZCA6IEFNWk4sIGJsYWNrIEFEQkUsIGdyZWVuOiBKTkosIGJsdWU6IDMgTW9udGggVCBCaWxsDQo=