knitr::opts_chunk$set(
    echo = TRUE,
    message = FALSE,
    warning = FALSE
)
library(zoo)
library(tseries)
library(lmtest)
library(sandwich)
library(car)
library(ggplot2)
library(patchwork)
library(quantmod)

Na začiatok si stiahneme naše dáta a z nich vypočítame logaritmické výnosnosti, s ktorými následne budeme pracovať.

tickers <- c("AAPL", "GLD", "XLE", "SPY")   # Apple, Gold ETF, Energy ETF, S&P500 ETF
getSymbols(tickers, from = "2024-01-01", to = "2025-01-01")
[1] "AAPL" "GLD"  "XLE"  "SPY" 
data <- merge(Cl(AAPL), Cl(GLD), Cl(XLE), Cl(SPY))
colnames(data) <- tickers

ret <- na.omit(diff(log(data)))
colnames(ret) <- paste0(colnames(ret), "_ret")

ret_df <- na.omit(as.data.frame(ret))
head(ret_df)
op <- par(mfrow = c(2, 2), mar = c(4,4,2,1))
for (col in names(ret_df)) {
  boxplot(ret_df[[col]], main = col, xlab = "Value", col = "purple")
}
par(op)

Boxploty znázorňujú rozdelenie denných logaritmických výnosov aktív AAPL, GLD, XLE a SPY. Všetky výnosy sa sústreďujú okolo nuly, pričom najväčšiu volatilitu vykazuje AAPL a najstabilnejšie hodnoty má GLD. Mierne odľahlé body predstavujú extrémne pohyby na trhu, no celkové rozdelenie je vyvážené a vhodné na ďalšiu analýzu.

model <- lm(AAPL_ret ~ GLD_ret + XLE_ret + SPY_ret, data = ret_df)
summary(model)

Call:
lm(formula = AAPL_ret ~ GLD_ret + XLE_ret + SPY_ret, data = ret_df)

Residuals:
      Min        1Q    Median        3Q       Max 
-0.044288 -0.006673 -0.000362  0.005717  0.066928 

Coefficients:
              Estimate Std. Error t value Pr(>|t|)    
(Intercept)  0.0003073  0.0007477   0.411   0.6814    
GLD_ret      0.0028525  0.0818840   0.035   0.9722    
XLE_ret     -0.1950403  0.0705065  -2.766   0.0061 ** 
SPY_ret      1.0396059  0.1007431  10.319   <2e-16 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 0.01173 on 247 degrees of freedom
Multiple R-squared:  0.3122,    Adjusted R-squared:  0.3038 
F-statistic: 37.37 on 3 and 247 DF,  p-value: < 2.2e-16

Model vysvetľuje závislosť výnosu akcie AAPL od výnosov GLD, XLE a SPY. Z výsledkov vyplýva, že štatisticky významné premenné sú XLE (p = 0,0061) a SPY (p < 0,001). Premenná GLD významný vplyv nemá. Kladný koeficient pri SPY (1,04) potvrdzuje silnú väzbu akcie Apple na celkový trh, zatiaľ čo záporný koeficient pri XLE (-0,20) naznačuje mierny opačný vzťah s energetickým sektorom. Hodnota R^2=0,31 znamená, že model vysvetľuje približne 31 % variability výnosov akcie Apple.

op <- par(mfrow = c(2,2))
plot(model)
par(op)

library(ggfortify)

autoplot(model, 
         which = 1:4, 
         colour = "#c8a2c8",          # svetlofialové body
         smooth.colour = "#9d4edd",   # jemnejšia fialová LOESS čiara
         smooth.linetype = "solid",
         size = 2,
         label.size = 3) +
  theme_minimal(base_size = 13) +
  theme(
    plot.title = element_text(color = "#5a189a", face = "bold", hjust = 0.5),
    strip.text = element_text(color = "#5a189a", face = "bold"),
    panel.grid.minor = element_blank(),
    panel.background = element_rect(fill = "white", color = NA),
    plot.background = element_rect(fill = "white", color = NA)
  )

Diagnostické grafy ukazujú, že model je dobre špecifikovaný. Reziduá sú rozložené rovnomerne okolo nuly, čo potvrdzuje absenciu výraznej systematickej chyby. Q-Q graf naznačuje len mierne odchýlky od normality na okrajoch rozdelenia. Graf Scale-Location potvrdzuje približne konštantnú varianciu rezíduí (homoskedasticitu) a graf Residuals vs Leverage neodhalil žiadne významné vplyvné pozorovania. Celkovo možno model považovať za stabilný a spoľahlivý.Graf Cook’s distance naznačuje, že žiadne pozorovanie výrazne neovplyvňuje stabilitu modelu, keďže všetky hodnoty zostávajú bezpečne pod hranicou významného vplyvu.

# Jarque–Bera
jb_test <- tseries::jarque.bera.test(residuals(model)); jb_test

    Jarque Bera Test

data:  residuals(model)
X-squared = 279.24, df = 2, p-value < 2.2e-16
# Outlier test (Bonferroni)
outlier_car <- car::outlierTest(model); outlier_car

Jarque-Bera test ukázal, že reziduá modelu nie sú normálne rozdelené (p-hodnota < 0,001), čo naznačuje prítomnosť extrémnych hodnôt. Test odľahlých pozorovaní (Bonferroni) identifikoval tri dni – 21. 3. 2024, 3. 5. 2024 a 11. 6. 2024 – ako štatisticky významné odľahlé pozorovania, ktoré mohli ovplyvniť výsledky regresie.

model2 <- lm(AAPL_ret ~ I(log(1 + SPY_ret)) + XLE_ret, data = ret_df)
summary(model2)

Call:
lm(formula = AAPL_ret ~ I(log(1 + SPY_ret)) + XLE_ret, data = ret_df)

Residuals:
      Min        1Q    Median        3Q       Max 
-0.044321 -0.006706 -0.000423  0.005711  0.066901 

Coefficients:
                      Estimate Std. Error t value Pr(>|t|)    
(Intercept)          0.0003426  0.0007431   0.461  0.64516    
I(log(1 + SPY_ret))  1.0402443  0.0980078  10.614  < 2e-16 ***
XLE_ret             -0.1953592  0.0696616  -2.804  0.00544 ** 
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 0.0117 on 248 degrees of freedom
Multiple R-squared:  0.313, Adjusted R-squared:  0.3074 
F-statistic: 56.48 on 2 and 248 DF,  p-value: < 2.2e-16
op <- par(mfrow = c(2,2))
plot(model2)
par(op)


# Normalita + outliers pre model2
tseries::jarque.bera.test(residuals(model2))

    Jarque Bera Test

data:  residuals(model2)
X-squared = 280.67, df = 2, p-value < 2.2e-16
car::outlierTest(model2)
autoplot(model2, 
         which = 1:4, 
         colour = "#c8a2c8",          # svetlofialové body
         smooth.colour = "#9d4edd",   # jemnejšia fialová LOESS čiara
         smooth.linetype = "solid",
         size = 2,
         label.size = 3) +
  theme_minimal(base_size = 13) +
  theme(
    plot.title = element_text(color = "#5a189a", face = "bold", hjust = 0.5),
    strip.text = element_text(color = "#5a189a", face = "bold"),
    panel.grid.minor = element_blank(),
    panel.background = element_rect(fill = "white", color = NA),
    plot.background = element_rect(fill = "white", color = NA)
  )

Po úprave modelu (logaritmická transformácia SPY_ret) sa štruktúra rezíduí zlepšila, hoci Jarque-Bera test stále naznačuje mierne odchýlky od normality. Outlier test opäť identifikoval rovnaké tri dni – 21. 3. 2024, 3. 5. 2024 a 11. 6. 2024 – ako odľahlé pozorovania, avšak ich vplyv na model je už menší. Celkovo možno povedať, že transformácia prispela k stabilnejšiemu a robustnejšiemu modelu.

# Pôvodný model
p1 <- ggplot(ret_df, aes(x = SPY_ret, y = resid(model)^2)) +
  geom_point(alpha = 0.6) +
  geom_smooth(method = "loess", se = FALSE, color = "purple") +
  labs(x = "SPY_ret", y = "Squared residuals", title = "Squared residuals vs SPY_ret") +
  theme_minimal()

p2 <- ggplot(ret_df, aes(x = XLE_ret, y = resid(model)^2)) +
  geom_point(alpha = 0.6) +
  geom_smooth(method = "loess", se = FALSE, color = "purple") +
  labs(x = "XLE_ret", y = "Squared residuals", title = "Squared residuals vs XLE_ret") +
  theme_minimal()

p1 + p2

Grafy zobrazujú závislosť štvorcov rezíduí od výnosov SPY_ret a XLE_ret s cieľom overiť prítomnosť heteroskedasticity. Červená vyhladzovacia krivka je takmer vodorovná, čo naznačuje, že rozptyl rezíduí zostáva približne konštantný pri rôznych hodnotách oboch premenných. Nevidíme žiadny rozširujúci sa alebo zužujúci tvar, preto možno konštatovať, že v modeli sa heteroskedasticita nevyskytuje.

# Upravený model (model2) s log(1+SPY_ret)
p3 <- ggplot(ret_df, aes(x = log(1 + SPY_ret), y = resid(model2)^2)) +
  geom_point(alpha = 0.6) +
  geom_smooth(method = "loess", se = FALSE, color = "purple") +
  labs(x = "log(1 + SPY_ret)", y = "Squared residuals", title = "Residuals vs log(1+SPY_ret)") +
  theme_minimal()

p4 <- ggplot(ret_df, aes(x = XLE_ret, y = resid(model2)^2)) +
  geom_point(alpha = 0.6) +
  geom_smooth(method = "loess", se = FALSE, color = "purple") +
  labs(x = "XLE_ret", y = "Squared residuals", title = "Residuals vs XLE_ret (model2)") +
  theme_minimal()

p3 + p4

Grafy zobrazujú závislosť štvorcov rezíduí od premenných log(1 + SPY_ret) a XLE_ret po logaritmickej transformácii v modeli 2. Červená vyhladzovacia krivka zostáva takmer vodorovná, čo naznačuje, že rozptyl rezíduí sa s hodnotami vysvetľujúcich premenných výrazne nemení. Z toho vyplýva, že transformácia úspešne odstránila heteroskedasticitu a model má konštantný rozptyl chýb.

lmtest::bptest(model)

    studentized Breusch-Pagan test

data:  model
BP = 0.48024, df = 3, p-value = 0.9232
lmtest::bptest(model2)

    studentized Breusch-Pagan test

data:  model2
BP = 0.48542, df = 2, p-value = 0.7845

Výsledky Breusch–Pagan testu pre oba modely ukazujú, že p-hodnoty (0,9232 a 0,7845) sú výrazne vyššie než 0,05. To znamená, že nezamietame nulovú hypotézu o homoskedasticite rezíduí. Inými slovami, rozptyl chýb je konštantný a v modeloch nie je prítomná heteroskedasticita, čo potvrdzuje ich spoľahlivosť.

# Robustné SE pre model2 (doporučené reportovať)
lmtest::coeftest(model2, vcov = sandwich::vcovHC(model2, type = "HC1"))

t test of coefficients:

                       Estimate  Std. Error t value  Pr(>|t|)    
(Intercept)          0.00034261  0.00073427  0.4666  0.641193    
I(log(1 + SPY_ret))  1.04024435  0.09913013 10.4937 < 2.2e-16 ***
XLE_ret             -0.19535922  0.06000130 -3.2559  0.001288 ** 
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Tabuľka zobrazuje výsledky robustnej regresie s upravenými smerodajnými chybami (HC1), ktoré zohľadňujú možnú heteroskedasticitu.

Premenná log(1 + SPY_ret) má silne pozitívny a štatisticky významný vplyv (p < 0.001) na výnos akcie AAPL, čo znamená, že Apple sa správa veľmi podobne ako trhový index SPY. Premenná XLE_ret má negatívny a štatisticky významný vplyv (p < 0.01), teda rast energetického sektora súvisí s miernym poklesom výnosov Apple. Konštanta (intercept) nie je štatisticky významná, čo naznačuje, že pri nulových výnosoch SPY a XLE je priemerný výnos AAPL blízky nule.

# Durbin–Watson (autokorelácia rezíduí)
lmtest::dwtest(model2)

    Durbin-Watson test

data:  model2
DW = 2.0406, p-value = 0.6239
alternative hypothesis: true autocorrelation is greater than 0
# RESET (nelinearity/špecifikácia)
lmtest::resettest(model2, power = 2:3, type = "fitted")

    RESET test

data:  model2
RESET = 1.3159, df1 = 2, df2 = 246, p-value = 0.2701
# VIF (multikolinearita)
car::vif(model2)
I(log(1 + SPY_ret))             XLE_ret 
           1.109609            1.109609 

Výsledky doplnkových testov potvrdzujú, že vytvorený model je štatisticky spoľahlivý a správne špecifikovaný. Hodnota Durbin–Watsonovho testu (DW = 2.04, p = 0.62) naznačuje, že v rezíduách sa nenachádza autokorelácia, čo znamená, že chyby nie sú navzájom závislé a model spĺňa predpoklad nezávislosti rezíduí. RESET test s p-hodnotou 0.27 nepreukázal žiadne problémy so špecifikáciou modelu, teda model má správne zvolenú funkčnú formu a nie je potrebné pridávať nelineárne členy. Výsledky VIF (Variance Inflation Factor) s hodnotou 1.1 pre obe vysvetľujúce premenné potvrdzujú, že medzi nimi neexistuje multikolinearita. Celkovo možno konštatovať, že model je dobre špecifikovaný, rezíduá sú nezávislé a vysvetľujúce premenné nie sú navzájom závislé.

library(knitr)
library(dplyr)

results <- data.frame(
  Test = c("Durbin–Watson", "RESET", "VIF (I(log(1+SPY_ret)))", "VIF (XLE_ret)"),
  Statistic = c(2.0406, 1.3159, 1.1096, 1.1096),
  `p-hodnota` = c(0.6239, 0.2701, NA, NA),
  Interpretácia = c(
    "Bez autokorelácie rezíduí",
    "Bez problémov so špecifikáciou modelu",
    "Žiadna multikolinearita",
    "Žiadna multikolinearita"
  )
)

kable(results, caption = "Súhrn doplnkových diagnostických testov pre model2")
Súhrn doplnkových diagnostických testov pre model2
Test Statistic p.hodnota Interpretácia
Durbin–Watson 2.0406 0.6239 Bez autokorelácie rezíduí
RESET 1.3159 0.2701 Bez problémov so špecifikáciou modelu
VIF (I(log(1+SPY_ret))) 1.1096 NA Žiadna multikolinearita
VIF (XLE_ret) 1.1096 NA Žiadna multikolinearita

Tabuľka zobrazuje výsledky doplnkových diagnostických testov pre model 2. Durbin–Watson test potvrdzuje, že rezíduá nie sú autokorelované. RESET test ukazuje, že model je správne špecifikovaný a neobsahuje nelineárne chyby. Hodnoty VIF sú veľmi nízke, čo znamená, že medzi vysvetľujúcimi premennými neexistuje multikolinearita. Celkovo možno model považovať za stabilný a štatisticky spoľahlivý.

LS0tCnRpdGxlOiAixaBpZXN0YSDDumxvaGEiCmF1dGhvcjogJyBNaXJvc2xhdmEgTWVkdmVja8OhICA8YnI+JwpkYXRlOiAiU2VwdGVtYmVyIDIwMjUiCm91dHB1dDoKICBodG1sX25vdGVib29rOgogICAgdG9jOiB0cnVlCiAgICB0b2NfZmxvYXQ6IHRydWUKICAgIHRoZW1lOiB1bml0ZWQKICAgIGhpZ2hsaWdodDogdGFuZ28KICBodG1sX2RvY3VtZW50OgogICAgdG9jOiB0cnVlCiAgICBkZl9wcmludDogcGFnZWQKZWRpdG9yX29wdGlvbnM6CiAgbWFya2Rvd246CiAgICB3cmFwOiA3MgotLS0KYGBge3J9CmtuaXRyOjpvcHRzX2NodW5rJHNldCgKICAgIGVjaG8gPSBUUlVFLAogICAgbWVzc2FnZSA9IEZBTFNFLAogICAgd2FybmluZyA9IEZBTFNFCikKYGBgCgo8c3R5bGU+Ci8qIENlbGtvdsOpIHBvemFkaWUgKi8KYm9keSB7CiAgYmFja2dyb3VuZDogbGluZWFyLWdyYWRpZW50KDEzNWRlZywgI2YzZThmZiwgI2U5ZDVmZiwgI2VkZTlmZSk7CiAgZm9udC1mYW1pbHk6ICdIZWx2ZXRpY2EgTmV1ZScsIEhlbHZldGljYSwgQXJpYWwsIHNhbnMtc2VyaWY7CiAgY29sb3I6ICMyZTA4NTQ7CiAgbGluZS1oZWlnaHQ6IDEuNzsKICBwYWRkaW5nOiAyNXB4Owp9CgovKiBIbGF2bsOpIG5hZHBpc3kgKi8KaDEsIGgyLCBoMyB7CiAgZm9udC1mYW1pbHk6ICdIZWx2ZXRpY2EgTmV1ZScsIEhlbHZldGljYSwgQXJpYWwsIHNhbnMtc2VyaWY7CiAgbGV0dGVyLXNwYWNpbmc6IDAuNXB4OwogIGNvbG9yOiAjNGIwMDgyOyAvKiB0bWF2b2ZpYWxvdsOhICovCiAgdGV4dC1zaGFkb3c6IDBweCAxcHggMnB4ICNlOWQ1ZmY7Cn0KPC9zdHlsZT4KCmBgYHtyfQpsaWJyYXJ5KHpvbykKbGlicmFyeSh0c2VyaWVzKQpsaWJyYXJ5KGxtdGVzdCkKbGlicmFyeShzYW5kd2ljaCkKbGlicmFyeShjYXIpCmxpYnJhcnkoZ2dwbG90MikKbGlicmFyeShwYXRjaHdvcmspCmxpYnJhcnkocXVhbnRtb2QpCmBgYAogICAgIApOYSB6YcSNaWF0b2sgc2kgc3RpYWhuZW1lIG5hxaFlIGTDoXRhIGEgeiBuaWNoIHZ5cG/EjcOtdGFtZSBsb2dhcml0bWlja8OpIHbDvW5vc25vc3RpLCBzIGt0b3LDvW1pIG7DoXNsZWRuZSBidWRlbWUgcHJhY292YcWlLiAgICAgIApgYGB7cn0KdGlja2VycyA8LSBjKCJBQVBMIiwgIkdMRCIsICJYTEUiLCAiU1BZIikgICAjIEFwcGxlLCBHb2xkIEVURiwgRW5lcmd5IEVURiwgUyZQNTAwIEVURgpnZXRTeW1ib2xzKHRpY2tlcnMsIGZyb20gPSAiMjAyNC0wMS0wMSIsIHRvID0gIjIwMjUtMDEtMDEiKQoKZGF0YSA8LSBtZXJnZShDbChBQVBMKSwgQ2woR0xEKSwgQ2woWExFKSwgQ2woU1BZKSkKY29sbmFtZXMoZGF0YSkgPC0gdGlja2VycwoKcmV0IDwtIG5hLm9taXQoZGlmZihsb2coZGF0YSkpKQpjb2xuYW1lcyhyZXQpIDwtIHBhc3RlMChjb2xuYW1lcyhyZXQpLCAiX3JldCIpCgpyZXRfZGYgPC0gbmEub21pdChhcy5kYXRhLmZyYW1lKHJldCkpCmhlYWQocmV0X2RmKQpgYGAKICAgICAKYGBge3J9Cm9wIDwtIHBhcihtZnJvdyA9IGMoMiwgMiksIG1hciA9IGMoNCw0LDIsMSkpCmZvciAoY29sIGluIG5hbWVzKHJldF9kZikpIHsKICBib3hwbG90KHJldF9kZltbY29sXV0sIG1haW4gPSBjb2wsIHhsYWIgPSAiVmFsdWUiLCBjb2wgPSAicHVycGxlIikKfQpwYXIob3ApCmBgYAogICAgIApCb3hwbG90eSB6bsOhem9yxYh1asO6IHJvemRlbGVuaWUgZGVubsO9Y2ggbG9nYXJpdG1pY2vDvWNoIHbDvW5vc292IGFrdMOtdiBBQVBMLCBHTEQsIFhMRSBhIFNQWS4gVsWhZXRreSB2w71ub3N5IHNhIHPDunN0cmXEj3Vqw7ogb2tvbG8gbnVseSwgcHJpxI1vbSBuYWp2w6TEjcWhaXUgdm9sYXRpbGl0dSB2eWthenVqZSBBQVBMIGEgbmFqc3RhYmlsbmVqxaFpZSBob2Rub3R5IG3DoSBHTEQuIE1pZXJuZSBvZMS+YWhsw6kgYm9keSBwcmVkc3RhdnVqw7ogZXh0csOpbW5lIHBvaHlieSBuYSB0cmh1LCBubyBjZWxrb3bDqSByb3pkZWxlbmllIGplIHZ5dsOhxb5lbsOpIGEgdmhvZG7DqSBuYSDEj2FsxaFpdSBhbmFsw716dS4gICAgIApgYGB7cn0KbW9kZWwgPC0gbG0oQUFQTF9yZXQgfiBHTERfcmV0ICsgWExFX3JldCArIFNQWV9yZXQsIGRhdGEgPSByZXRfZGYpCnN1bW1hcnkobW9kZWwpCmBgYAogICAgIApNb2RlbCB2eXN2ZXTEvnVqZSB6w6F2aXNsb3PFpSB2w71ub3N1IGFrY2llIEFBUEwgb2QgdsO9bm9zb3YgR0xELCBYTEUgYSBTUFkuIFogdsO9c2xlZGtvdiB2eXBsw712YSwgxb5lIMWhdGF0aXN0aWNreSB2w716bmFtbsOpIHByZW1lbm7DqSBzw7ogWExFIChwID0gMCwwMDYxKSBhIFNQWSAocCA8IDAsMDAxKS4gUHJlbWVubsOhIEdMRCB2w716bmFtbsO9IHZwbHl2IG5lbcOhLiBLbGFkbsO9IGtvZWZpY2llbnQgcHJpIFNQWSAoMSwwNCkgcG90dnJkenVqZSBzaWxuw7ogdsOkemJ1IGFrY2llIEFwcGxlIG5hIGNlbGtvdsO9IHRyaCwgemF0aWHEviDEjW8gesOhcG9ybsO9IGtvZWZpY2llbnQgcHJpIFhMRSAoLTAsMjApIG5hem5hxI11amUgbWllcm55IG9wYcSNbsO9IHZ6xaVhaCBzIGVuZXJnZXRpY2vDvW0gc2VrdG9yb20uIEhvZG5vdGEgUl4yPTAsMzEgem5hbWVuw6EsIMW+ZSBtb2RlbCB2eXN2ZXTEvnVqZSBwcmlibGnFvm5lIDMxICUgdmFyaWFiaWxpdHkgdsO9bm9zb3YgYWtjaWUgQXBwbGUuCiAgICAgCmBgYHtyfQpvcCA8LSBwYXIobWZyb3cgPSBjKDIsMikpCnBsb3QobW9kZWwpCnBhcihvcCkKYGBgCmBgYHtyfQpsaWJyYXJ5KGdnZm9ydGlmeSkKCmF1dG9wbG90KG1vZGVsLCAKICAgICAgICAgd2hpY2ggPSAxOjQsIAogICAgICAgICBjb2xvdXIgPSAiI2M4YTJjOCIsICAgICAgICAgICMgc3ZldGxvZmlhbG92w6kgYm9keQogICAgICAgICBzbW9vdGguY29sb3VyID0gIiM5ZDRlZGQiLCAgICMgamVtbmVqxaFpYSBmaWFsb3bDoSBMT0VTUyDEjWlhcmEKICAgICAgICAgc21vb3RoLmxpbmV0eXBlID0gInNvbGlkIiwKICAgICAgICAgc2l6ZSA9IDIsCiAgICAgICAgIGxhYmVsLnNpemUgPSAzKSArCiAgdGhlbWVfbWluaW1hbChiYXNlX3NpemUgPSAxMykgKwogIHRoZW1lKAogICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChjb2xvciA9ICIjNWExODlhIiwgZmFjZSA9ICJib2xkIiwgaGp1c3QgPSAwLjUpLAogICAgc3RyaXAudGV4dCA9IGVsZW1lbnRfdGV4dChjb2xvciA9ICIjNWExODlhIiwgZmFjZSA9ICJib2xkIiksCiAgICBwYW5lbC5ncmlkLm1pbm9yID0gZWxlbWVudF9ibGFuaygpLAogICAgcGFuZWwuYmFja2dyb3VuZCA9IGVsZW1lbnRfcmVjdChmaWxsID0gIndoaXRlIiwgY29sb3IgPSBOQSksCiAgICBwbG90LmJhY2tncm91bmQgPSBlbGVtZW50X3JlY3QoZmlsbCA9ICJ3aGl0ZSIsIGNvbG9yID0gTkEpCiAgKQpgYGAKCkRpYWdub3N0aWNrw6kgZ3JhZnkgdWthenVqw7osIMW+ZSBtb2RlbCBqZSBkb2JyZSDFoXBlY2lmaWtvdmFuw70uIFJlemlkdcOhIHPDuiByb3psb8W+ZW7DqSByb3Zub21lcm5lIG9rb2xvIG51bHksIMSNbyBwb3R2cmR6dWplIGFic2VuY2l1IHbDvXJhem5laiBzeXN0ZW1hdGlja2VqIGNoeWJ5LiBRLVEgZ3JhZiBuYXpuYcSNdWplIGxlbiBtaWVybmUgb2RjaMO9bGt5IG9kIG5vcm1hbGl0eSBuYSBva3Jham9jaCByb3pkZWxlbmlhLiBHcmFmIFNjYWxlLUxvY2F0aW9uIHBvdHZyZHp1amUgcHJpYmxpxb5uZSBrb27FoXRhbnRuw7ogdmFyaWFuY2l1IHJlesOtZHXDrSAoaG9tb3NrZWRhc3RpY2l0dSkgYSBncmFmIFJlc2lkdWFscyB2cyBMZXZlcmFnZSBuZW9kaGFsaWwgxb5pYWRuZSB2w716bmFtbsOpIHZwbHl2bsOpIHBvem9yb3ZhbmlhLiBDZWxrb3ZvIG1vxb5ubyBtb2RlbCBwb3Zhxb5vdmHFpSB6YSBzdGFiaWxuw70gYSBzcG/EvmFobGl2w70uR3JhZiBDb29r4oCZcyBkaXN0YW5jZSBuYXpuYcSNdWplLCDFvmUgxb5pYWRuZSBwb3pvcm92YW5pZSB2w71yYXpuZSBuZW92cGx5dsWIdWplIHN0YWJpbGl0dSBtb2RlbHUsIGtlxI/FvmUgdsWhZXRreSBob2Rub3R5IHpvc3TDoXZhasO6IGJlenBlxI1uZSBwb2QgaHJhbmljb3UgdsO9em5hbW7DqWhvIHZwbHl2dS4KICAgIApgYGB7cn0KIyBKYXJxdWXigJNCZXJhCmpiX3Rlc3QgPC0gdHNlcmllczo6amFycXVlLmJlcmEudGVzdChyZXNpZHVhbHMobW9kZWwpKTsgamJfdGVzdAoKIyBPdXRsaWVyIHRlc3QgKEJvbmZlcnJvbmkpCm91dGxpZXJfY2FyIDwtIGNhcjo6b3V0bGllclRlc3QobW9kZWwpOyBvdXRsaWVyX2NhcgpgYGAKCkphcnF1ZS1CZXJhIHRlc3QgdWvDoXphbCwgxb5lIHJlemlkdcOhIG1vZGVsdSBuaWUgc8O6IG5vcm3DoWxuZSByb3pkZWxlbsOpIChwLWhvZG5vdGEgPCAwLDAwMSksIMSNbyBuYXpuYcSNdWplIHByw610b21ub3PFpSBleHRyw6ltbnljaCBob2Ruw7R0LiBUZXN0IG9kxL5haGzDvWNoIHBvem9yb3ZhbsOtIChCb25mZXJyb25pKSBpZGVudGlmaWtvdmFsIHRyaSBkbmkg4oCTIDIxLiAzLiAyMDI0LCAzLiA1LiAyMDI0IGEgMTEuIDYuIDIwMjQg4oCTIGFrbyDFoXRhdGlzdGlja3kgdsO9em5hbW7DqSBvZMS+YWhsw6kgcG96b3JvdmFuaWEsIGt0b3LDqSBtb2hsaSBvdnBseXZuacWlIHbDvXNsZWRreSByZWdyZXNpZS4gICAgCgpgYGB7cn0KbW9kZWwyIDwtIGxtKEFBUExfcmV0IH4gSShsb2coMSArIFNQWV9yZXQpKSArIFhMRV9yZXQsIGRhdGEgPSByZXRfZGYpCnN1bW1hcnkobW9kZWwyKQoKb3AgPC0gcGFyKG1mcm93ID0gYygyLDIpKQpwbG90KG1vZGVsMikKcGFyKG9wKQoKIyBOb3JtYWxpdGEgKyBvdXRsaWVycyBwcmUgbW9kZWwyCnRzZXJpZXM6OmphcnF1ZS5iZXJhLnRlc3QocmVzaWR1YWxzKG1vZGVsMikpCmNhcjo6b3V0bGllclRlc3QobW9kZWwyKQpgYGAKCmBgYHtyfQphdXRvcGxvdChtb2RlbDIsIAogICAgICAgICB3aGljaCA9IDE6NCwgCiAgICAgICAgIGNvbG91ciA9ICIjYzhhMmM4IiwgICAgICAgICAgIyBzdmV0bG9maWFsb3bDqSBib2R5CiAgICAgICAgIHNtb290aC5jb2xvdXIgPSAiIzlkNGVkZCIsICAgIyBqZW1uZWrFoWlhIGZpYWxvdsOhIExPRVNTIMSNaWFyYQogICAgICAgICBzbW9vdGgubGluZXR5cGUgPSAic29saWQiLAogICAgICAgICBzaXplID0gMiwKICAgICAgICAgbGFiZWwuc2l6ZSA9IDMpICsKICB0aGVtZV9taW5pbWFsKGJhc2Vfc2l6ZSA9IDEzKSArCiAgdGhlbWUoCiAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGNvbG9yID0gIiM1YTE4OWEiLCBmYWNlID0gImJvbGQiLCBoanVzdCA9IDAuNSksCiAgICBzdHJpcC50ZXh0ID0gZWxlbWVudF90ZXh0KGNvbG9yID0gIiM1YTE4OWEiLCBmYWNlID0gImJvbGQiKSwKICAgIHBhbmVsLmdyaWQubWlub3IgPSBlbGVtZW50X2JsYW5rKCksCiAgICBwYW5lbC5iYWNrZ3JvdW5kID0gZWxlbWVudF9yZWN0KGZpbGwgPSAid2hpdGUiLCBjb2xvciA9IE5BKSwKICAgIHBsb3QuYmFja2dyb3VuZCA9IGVsZW1lbnRfcmVjdChmaWxsID0gIndoaXRlIiwgY29sb3IgPSBOQSkKICApCmBgYApQbyDDunByYXZlIG1vZGVsdSAobG9nYXJpdG1pY2vDoSB0cmFuc2Zvcm3DoWNpYSBTUFlfcmV0KSBzYSDFoXRydWt0w7pyYSByZXrDrWR1w60gemxlcMWhaWxhLCBob2NpIEphcnF1ZS1CZXJhIHRlc3Qgc3TDoWxlIG5hem5hxI11amUgbWllcm5lIG9kY2jDvWxreSBvZCBub3JtYWxpdHkuIE91dGxpZXIgdGVzdCBvcMOkxaUgaWRlbnRpZmlrb3ZhbCByb3ZuYWvDqSB0cmkgZG5pIOKAkyAyMS4gMy4gMjAyNCwgMy4gNS4gMjAyNCBhIDExLiA2LiAyMDI0IOKAkyBha28gb2TEvmFobMOpIHBvem9yb3ZhbmlhLCBhdsWhYWsgaWNoIHZwbHl2IG5hIG1vZGVsIGplIHXFviBtZW7FocOtLiBDZWxrb3ZvIG1vxb5ubyBwb3ZlZGHFpSwgxb5lIHRyYW5zZm9ybcOhY2lhIHByaXNwZWxhIGsgc3RhYmlsbmVqxaFpZW11IGEgcm9idXN0bmVqxaFpZW11IG1vZGVsdS4gIAoKYGBge3J9CiMgUMO0dm9kbsO9IG1vZGVsCnAxIDwtIGdncGxvdChyZXRfZGYsIGFlcyh4ID0gU1BZX3JldCwgeSA9IHJlc2lkKG1vZGVsKV4yKSkgKwogIGdlb21fcG9pbnQoYWxwaGEgPSAwLjYpICsKICBnZW9tX3Ntb290aChtZXRob2QgPSAibG9lc3MiLCBzZSA9IEZBTFNFLCBjb2xvciA9ICJwdXJwbGUiKSArCiAgbGFicyh4ID0gIlNQWV9yZXQiLCB5ID0gIlNxdWFyZWQgcmVzaWR1YWxzIiwgdGl0bGUgPSAiU3F1YXJlZCByZXNpZHVhbHMgdnMgU1BZX3JldCIpICsKICB0aGVtZV9taW5pbWFsKCkKCnAyIDwtIGdncGxvdChyZXRfZGYsIGFlcyh4ID0gWExFX3JldCwgeSA9IHJlc2lkKG1vZGVsKV4yKSkgKwogIGdlb21fcG9pbnQoYWxwaGEgPSAwLjYpICsKICBnZW9tX3Ntb290aChtZXRob2QgPSAibG9lc3MiLCBzZSA9IEZBTFNFLCBjb2xvciA9ICJwdXJwbGUiKSArCiAgbGFicyh4ID0gIlhMRV9yZXQiLCB5ID0gIlNxdWFyZWQgcmVzaWR1YWxzIiwgdGl0bGUgPSAiU3F1YXJlZCByZXNpZHVhbHMgdnMgWExFX3JldCIpICsKICB0aGVtZV9taW5pbWFsKCkKCnAxICsgcDIKYGBgCkdyYWZ5IHpvYnJhenVqw7ogesOhdmlzbG9zxaUgxaF0dm9yY292IHJlesOtZHXDrSBvZCB2w71ub3NvdiBTUFlfcmV0IGEgWExFX3JldCBzIGNpZcS+b20gb3ZlcmnFpSBwcsOtdG9tbm9zxaUgaGV0ZXJvc2tlZGFzdGljaXR5LiDEjGVydmVuw6EgdnlobGFkem92YWNpYSBrcml2a2EgamUgdGFrbWVyIHZvZG9yb3Zuw6EsIMSNbyBuYXpuYcSNdWplLCDFvmUgcm96cHR5bCByZXrDrWR1w60gem9zdMOhdmEgcHJpYmxpxb5uZSBrb27FoXRhbnRuw70gcHJpIHLDtHpueWNoIGhvZG5vdMOhY2ggb2JvY2ggcHJlbWVubsO9Y2guIE5ldmlkw61tZSDFvmlhZG55IHJvesWhaXJ1asO6Y2kgc2EgYWxlYm8genXFvnVqw7pjaSB0dmFyLCBwcmV0byBtb8W+bm8ga29uxaF0YXRvdmHFpSwgxb5lIHYgbW9kZWxpIHNhIGhldGVyb3NrZWRhc3RpY2l0YSBuZXZ5c2t5dHVqZS4KYGBge3J9CiMgVXByYXZlbsO9IG1vZGVsIChtb2RlbDIpIHMgbG9nKDErU1BZX3JldCkKcDMgPC0gZ2dwbG90KHJldF9kZiwgYWVzKHggPSBsb2coMSArIFNQWV9yZXQpLCB5ID0gcmVzaWQobW9kZWwyKV4yKSkgKwogIGdlb21fcG9pbnQoYWxwaGEgPSAwLjYpICsKICBnZW9tX3Ntb290aChtZXRob2QgPSAibG9lc3MiLCBzZSA9IEZBTFNFLCBjb2xvciA9ICJwdXJwbGUiKSArCiAgbGFicyh4ID0gImxvZygxICsgU1BZX3JldCkiLCB5ID0gIlNxdWFyZWQgcmVzaWR1YWxzIiwgdGl0bGUgPSAiUmVzaWR1YWxzIHZzIGxvZygxK1NQWV9yZXQpIikgKwogIHRoZW1lX21pbmltYWwoKQoKcDQgPC0gZ2dwbG90KHJldF9kZiwgYWVzKHggPSBYTEVfcmV0LCB5ID0gcmVzaWQobW9kZWwyKV4yKSkgKwogIGdlb21fcG9pbnQoYWxwaGEgPSAwLjYpICsKICBnZW9tX3Ntb290aChtZXRob2QgPSAibG9lc3MiLCBzZSA9IEZBTFNFLCBjb2xvciA9ICJwdXJwbGUiKSArCiAgbGFicyh4ID0gIlhMRV9yZXQiLCB5ID0gIlNxdWFyZWQgcmVzaWR1YWxzIiwgdGl0bGUgPSAiUmVzaWR1YWxzIHZzIFhMRV9yZXQgKG1vZGVsMikiKSArCiAgdGhlbWVfbWluaW1hbCgpCgpwMyArIHA0CmBgYApHcmFmeSB6b2JyYXp1asO6IHrDoXZpc2xvc8WlIMWhdHZvcmNvdiByZXrDrWR1w60gb2QgcHJlbWVubsO9Y2ggbG9nKDEgKyBTUFlfcmV0KSBhIFhMRV9yZXQgcG8gbG9nYXJpdG1pY2tlaiB0cmFuc2Zvcm3DoWNpaSB2IG1vZGVsaSAyLiDEjGVydmVuw6EgdnlobGFkem92YWNpYSBrcml2a2Egem9zdMOhdmEgdGFrbWVyIHZvZG9yb3Zuw6EsIMSNbyBuYXpuYcSNdWplLCDFvmUgcm96cHR5bCByZXrDrWR1w60gc2EgcyBob2Rub3RhbWkgdnlzdmV0xL51asO6Y2ljaCBwcmVtZW5uw71jaCB2w71yYXpuZSBuZW1lbsOtLiBaIHRvaG8gdnlwbMO9dmEsIMW+ZSB0cmFuc2Zvcm3DoWNpYSDDunNwZcWhbmUgb2RzdHLDoW5pbGEgaGV0ZXJvc2tlZGFzdGljaXR1IGEgbW9kZWwgbcOhIGtvbsWhdGFudG7DvSByb3pwdHlsIGNow71iLgpgYGB7cn0KbG10ZXN0OjpicHRlc3QobW9kZWwpCmxtdGVzdDo6YnB0ZXN0KG1vZGVsMikKYGBgCgpWw71zbGVka3kgQnJldXNjaOKAk1BhZ2FuIHRlc3R1IHByZSBvYmEgbW9kZWx5IHVrYXp1asO6LCDFvmUgcC1ob2Rub3R5ICgwLDkyMzIgYSAwLDc4NDUpIHPDuiB2w71yYXpuZSB2ecWhxaFpZSBuZcW+IDAsMDUuIFRvIHpuYW1lbsOhLCDFvmUgbmV6YW1pZXRhbWUgbnVsb3bDuiBoeXBvdMOpenUgbyBob21vc2tlZGFzdGljaXRlIHJlesOtZHXDrS4gSW7DvW1pIHNsb3ZhbWksIHJvenB0eWwgY2jDvWIgamUga29uxaF0YW50bsO9IGEgdiBtb2RlbG9jaCBuaWUgamUgcHLDrXRvbW7DoSBoZXRlcm9za2VkYXN0aWNpdGEsIMSNbyBwb3R2cmR6dWplIGljaCBzcG/EvmFobGl2b3PFpS4KYGBge3J9CiMgUm9idXN0bsOpIFNFIHByZSBtb2RlbDIgKGRvcG9ydcSNZW7DqSByZXBvcnRvdmHFpSkKbG10ZXN0Ojpjb2VmdGVzdChtb2RlbDIsIHZjb3YgPSBzYW5kd2ljaDo6dmNvdkhDKG1vZGVsMiwgdHlwZSA9ICJIQzEiKSkKYGBgClRhYnXEvmthIHpvYnJhenVqZSB2w71zbGVka3kgcm9idXN0bmVqIHJlZ3Jlc2llIHMgdXByYXZlbsO9bWkgc21lcm9kYWpuw71taSBjaHliYW1pIChIQzEpLCBrdG9yw6kgem9oxL5hZMWIdWrDuiBtb8W+bsO6IGhldGVyb3NrZWRhc3RpY2l0dS4KClByZW1lbm7DoSBsb2coMSArIFNQWV9yZXQpIG3DoSBzaWxuZSBwb3ppdMOtdm55IGEgxaF0YXRpc3RpY2t5IHbDvXpuYW1uw70gdnBseXYgKHAgPCAwLjAwMSkgbmEgdsO9bm9zIGFrY2llIEFBUEwsIMSNbyB6bmFtZW7DoSwgxb5lIEFwcGxlIHNhIHNwcsOhdmEgdmXEvm1pIHBvZG9ibmUgYWtvIHRyaG92w70gaW5kZXggU1BZLgpQcmVtZW5uw6EgWExFX3JldCBtw6EgbmVnYXTDrXZueSBhIMWhdGF0aXN0aWNreSB2w716bmFtbsO9IHZwbHl2IChwIDwgMC4wMSksIHRlZGEgcmFzdCBlbmVyZ2V0aWNrw6lobyBzZWt0b3JhIHPDunZpc8OtIHMgbWllcm55bSBwb2tsZXNvbSB2w71ub3NvdiBBcHBsZS4KS29uxaF0YW50YSAoaW50ZXJjZXB0KSBuaWUgamUgxaF0YXRpc3RpY2t5IHbDvXpuYW1uw6EsIMSNbyBuYXpuYcSNdWplLCDFvmUgcHJpIG51bG92w71jaCB2w71ub3NvY2ggU1BZIGEgWExFIGplIHByaWVtZXJuw70gdsO9bm9zIEFBUEwgYmzDrXpreSBudWxlLgpgYGB7cn0KIyBEdXJiaW7igJNXYXRzb24gKGF1dG9rb3JlbMOhY2lhIHJlesOtZHXDrSkKbG10ZXN0Ojpkd3Rlc3QobW9kZWwyKQoKIyBSRVNFVCAobmVsaW5lYXJpdHkvxaFwZWNpZmlrw6FjaWEpCmxtdGVzdDo6cmVzZXR0ZXN0KG1vZGVsMiwgcG93ZXIgPSAyOjMsIHR5cGUgPSAiZml0dGVkIikKCiMgVklGIChtdWx0aWtvbGluZWFyaXRhKQpjYXI6OnZpZihtb2RlbDIpCmBgYApWw71zbGVka3kgZG9wbG5rb3bDvWNoIHRlc3RvdiBwb3R2cmR6dWrDuiwgxb5lIHZ5dHZvcmVuw70gbW9kZWwgamUgxaF0YXRpc3RpY2t5IHNwb8S+YWhsaXbDvSBhIHNwcsOhdm5lIMWhcGVjaWZpa292YW7DvS4gSG9kbm90YSBEdXJiaW7igJNXYXRzb25vdmhvIHRlc3R1IChEVyA9IDIuMDQsIHAgPSAwLjYyKSBuYXpuYcSNdWplLCDFvmUgdiByZXrDrWR1w6FjaCBzYSBuZW5hY2jDoWR6YSBhdXRva29yZWzDoWNpYSwgxI1vIHpuYW1lbsOhLCDFvmUgY2h5YnkgbmllIHPDuiBuYXZ6w6Fqb20gesOhdmlzbMOpIGEgbW9kZWwgc3DEusWIYSBwcmVkcG9rbGFkIG5lesOhdmlzbG9zdGkgcmV6w61kdcOtLiBSRVNFVCB0ZXN0IHMgcC1ob2Rub3RvdSAwLjI3IG5lcHJldWvDoXphbCDFvmlhZG5lIHByb2Jsw6lteSBzbyDFoXBlY2lmaWvDoWNpb3UgbW9kZWx1LCB0ZWRhIG1vZGVsIG3DoSBzcHLDoXZuZSB6dm9sZW7DuiBmdW5rxI1uw7ogZm9ybXUgYSBuaWUgamUgcG90cmVibsOpIHByaWTDoXZhxaUgbmVsaW5lw6FybmUgxI1sZW55LiBWw71zbGVka3kgVklGIChWYXJpYW5jZSBJbmZsYXRpb24gRmFjdG9yKSBzIGhvZG5vdG91IDEuMSBwcmUgb2JlIHZ5c3ZldMS+dWrDumNlIHByZW1lbm7DqSBwb3R2cmR6dWrDuiwgxb5lIG1lZHppIG5pbWkgbmVleGlzdHVqZSBtdWx0aWtvbGluZWFyaXRhLiBDZWxrb3ZvIG1vxb5ubyBrb27FoXRhdG92YcWlLCDFvmUgbW9kZWwgamUgZG9icmUgxaFwZWNpZmlrb3ZhbsO9LCByZXrDrWR1w6Egc8O6IG5lesOhdmlzbMOpIGEgdnlzdmV0xL51asO6Y2UgcHJlbWVubsOpIG5pZSBzw7ogbmF2esOham9tIHrDoXZpc2zDqS4KYGBge3J9CmxpYnJhcnkoa25pdHIpCmxpYnJhcnkoZHBseXIpCgpyZXN1bHRzIDwtIGRhdGEuZnJhbWUoCiAgVGVzdCA9IGMoIkR1cmJpbuKAk1dhdHNvbiIsICJSRVNFVCIsICJWSUYgKEkobG9nKDErU1BZX3JldCkpKSIsICJWSUYgKFhMRV9yZXQpIiksCiAgU3RhdGlzdGljID0gYygyLjA0MDYsIDEuMzE1OSwgMS4xMDk2LCAxLjEwOTYpLAogIGBwLWhvZG5vdGFgID0gYygwLjYyMzksIDAuMjcwMSwgTkEsIE5BKSwKICBJbnRlcnByZXTDoWNpYSA9IGMoCiAgICAiQmV6IGF1dG9rb3JlbMOhY2llIHJlesOtZHXDrSIsCiAgICAiQmV6IHByb2Jsw6ltb3Ygc28gxaFwZWNpZmlrw6FjaW91IG1vZGVsdSIsCiAgICAixb1pYWRuYSBtdWx0aWtvbGluZWFyaXRhIiwKICAgICLFvWlhZG5hIG11bHRpa29saW5lYXJpdGEiCiAgKQopCgprYWJsZShyZXN1bHRzLCBjYXB0aW9uID0gIlPDumhybiBkb3BsbmtvdsO9Y2ggZGlhZ25vc3RpY2vDvWNoIHRlc3RvdiBwcmUgbW9kZWwyIikKYGBgClRhYnXEvmthIHpvYnJhenVqZSB2w71zbGVka3kgZG9wbG5rb3bDvWNoIGRpYWdub3N0aWNrw71jaCB0ZXN0b3YgcHJlIG1vZGVsIDIuIER1cmJpbuKAk1dhdHNvbiB0ZXN0IHBvdHZyZHp1amUsIMW+ZSByZXrDrWR1w6EgbmllIHPDuiBhdXRva29yZWxvdmFuw6kuIFJFU0VUIHRlc3QgdWthenVqZSwgxb5lIG1vZGVsIGplIHNwcsOhdm5lIMWhcGVjaWZpa292YW7DvSBhIG5lb2JzYWh1amUgbmVsaW5lw6FybmUgY2h5YnkuIEhvZG5vdHkgVklGIHPDuiB2ZcS+bWkgbsOtemtlLCDEjW8gem5hbWVuw6EsIMW+ZSBtZWR6aSB2eXN2ZXTEvnVqw7pjaW1pIHByZW1lbm7DvW1pIG5lZXhpc3R1amUgbXVsdGlrb2xpbmVhcml0YS4gQ2Vsa292byBtb8W+bm8gbW9kZWwgcG92YcW+b3ZhxaUgemEgc3RhYmlsbsO9IGEgxaF0YXRpc3RpY2t5IHNwb8S+YWhsaXbDvS4K