Budeme odhadovať model celkových cestovných nákladov v závislosti od dĺžky pobytu a veku cestujúcej osoby:

\[ \textbf{TotalCost} = \text{Accommodation cost} + \text{Transportation cost} \]

\[ \text{TotalCost} = \beta_0 + \beta_1 \cdot \text{Duration (days)} + \beta_2 \cdot \text{Traveler age} + u \]

Knižnice a nastavenie prostredia

library(zoo)
library(tseries)
library(lmtest)
library(sandwich)
library(car)
rm(list = ls())

Načítanie a príprava databázy

# načítanie dát 
data_raw <- read.csv("Travel_data.csv", sep = ";", header = TRUE, stringsAsFactors = FALSE)

# rýchla kontrola štruktúry
str(data_raw)
'data.frame':   137 obs. of  13 variables:
 $ Trip.ID             : int  1 2 3 4 5 6 7 8 9 10 ...
 $ Destination         : chr  "London, UK" "Phuket, Thailand" "Bali, Indonesia" "New York, USA" ...
 $ Start.date          : chr  "01/05/2023" "15/06/2023" "01/07/2023" "15/08/2023" ...
 $ End.date            : chr  "08/05/2023" "20/06/2023" "08/07/2023" "29/08/2023" ...
 $ Duration..days.     : int  7 5 7 14 7 5 10 7 7 7 ...
 $ Traveler.name       : chr  "John Smith" "Jane Doe" "David Lee" "Sarah Johnson" ...
 $ Traveler.age        : int  35 28 45 29 26 42 33 25 31 39 ...
 $ Traveler.gender     : chr  "Male" "Female" "Male" "Female" ...
 $ Traveler.nationality: chr  "American" "Canadian" "Korean" "British" ...
 $ Accommodation.type  : chr  "Hotel" "Resort" "Villa" "Hotel" ...
 $ Accommodation.cost  : int  1200 800 1000 2000 700 1500 500 900 1200 2500 ...
 $ Transportation.type : chr  "Flight" "Flight" "Flight" "Flight" ...
 $ Transportation.cost : int  600 500 700 1000 200 800 1200 600 200 800 ...
head(data_raw, 5)

Tvorba analytickej tabuľky

  • Vytvoríme TotalCost = Accommodation cost + Transportation cost
  • Pre istotu premenujeme stĺpce na syntakticky bezpečné názvy
  • Vyberieme len potrebné premenné do modelu
  • Urobíme imputáciu mediánom pre chýbajúce hodnoty (ak sa vyskytnú)
# bezpečné názvy stĺpcov
names(data_raw) <- make.names(names(data_raw))

# konverzie číselných premenných (ak by pri importe ostali ako text)
num_cols <- c("Duration..days.", "Traveler.age", "Accommodation.cost", "Transportation.cost")
for (cl in num_cols) {
  if (cl %in% names(data_raw)) {
    data_raw[[cl]] <- suppressWarnings(as.numeric(data_raw[[cl]]))
  }
}

# výpočet TotalCost
data_raw$TotalCost <- with(data_raw, Accommodation.cost + Transportation.cost)

# pracovná tabuľka len s potrebnými premennými
dat <- subset(data_raw, select = c(TotalCost, Duration..days., Traveler.age))

# imputácia mediánom pre NA
col_meds <- sapply(dat, function(x) if (is.numeric(x)) median(x, na.rm = TRUE) else NA)
for (cl in names(dat)) {
  if (is.numeric(dat[[cl]])) {
    nas <- is.na(dat[[cl]])
    if (any(nas)) dat[[cl]][nas] <- col_meds[cl]
  }
}

summary(dat)
   TotalCost     Duration..days. 
 Min.   :  200   Min.   : 5.000  
 1st Qu.: 1000   1st Qu.: 7.000  
 Median : 1400   Median : 7.000  
 Mean   : 1895   Mean   : 7.606  
 3rd Qu.: 1900   3rd Qu.: 8.000  
 Max.   :10500   Max.   :14.000  
  Traveler.age  
 Min.   :20.00  
 1st Qu.:28.00  
 Median :31.00  
 Mean   :33.18  
 3rd Qu.:38.00  
 Max.   :60.00  

Rýchla vizualizácia – boxploty

par(mfrow = c(1, 3), mar = c(4, 4, 2, 1))
boxplot(dat$TotalCost, main = "TotalCost", xlab = "", col = "lightblue")
boxplot(dat$Duration..days., main = "Duration (days)", xlab = "", col = "lightgreen")
boxplot(dat$Traveler.age, main = "Traveler age", xlab = "", col = "lightpink")
par(mfrow = c(1, 1))

Lineárna regresia (Model 1)

model1 <- lm(TotalCost ~ 1 + Duration..days. + Traveler.age, data = dat)
summary(model1)

Call:
lm(formula = TotalCost ~ 1 + Duration..days. + Traveler.age, 
    data = dat)

Residuals:
    Min      1Q  Median      3Q     Max 
-1742.6  -869.3  -483.8   111.0  8594.2 

Coefficients:
                Estimate Std. Error
(Intercept)     2401.686   1123.981
Duration..days. -103.226     98.862
Traveler.age       8.395     22.155
                t value Pr(>|t|)  
(Intercept)       2.137   0.0344 *
Duration..days.  -1.044   0.2983  
Traveler.age      0.379   0.7053  
---
Signif. codes:  
  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’
  0.1 ‘ ’ 1

Residual standard error: 1833 on 134 degrees of freedom
Multiple R-squared:  0.009965,  Adjusted R-squared:  -0.004811 
F-statistic: 0.6744 on 2 and 134 DF,  p-value: 0.5112

Diagnostické grafy

# Diagnostické grafy – vlastná verzia s lepšou mierkou
par(mfrow = c(2, 2), mar = c(4,4,2,1))

# 1️⃣ Residuals vs Fitted
plot(fitted(model1), residuals(model1), 
     pch = 19, col = "darkred",
     xlab = "Fitted values", ylab = "Residuals",
     main = "Residuals vs Fitted")
abline(h = 0, col = "gray40", lty = 2)

# 2️⃣ Q-Q plot
qqnorm(rstandard(model1), pch = 19, col = "darkblue",
       main = "Q-Q Plot of Standardized Residuals")
qqline(rstandard(model1), col = "gray40", lty = 2)

# 3️⃣ Scale-Location plot
plot(fitted(model1), sqrt(abs(rstandard(model1))),
     pch = 19, col = "darkgreen",
     xlab = "Fitted values", ylab = expression(sqrt("|Standardized residuals|")),
     main = "Scale-Location")
abline(h = 0, col = "gray40", lty = 2)

# 4️⃣ Residuals vs Leverage
plot(hatvalues(model1), rstandard(model1),
     pch = 19, col = "purple",
     xlab = "Leverage", ylab = "Standardized residuals",
     main = "Residuals vs Leverage")
abline(h = 0, col = "gray40", lty = 2)

par(mfrow = c(1, 1))

NA
NA

Testy normality a odľahlostí

res1 <- residuals(model1)

# Jarque–Bera test normality
jb1 <- jarque.bera.test(res1)
jb1

    Jarque Bera Test

data:  res1
X-squared = 628.91, df = 2, p-value <
2.2e-16
# Outlier test (Bonferroni)
out1 <- outlierTest(model1)
out1

Alternatívny model so zmiernením vplyvu odľahlých hodnôt (Model 2)

Ak sú náklady pozitívne a pravdepodobne prahovo posunuté a šikmé (pravostranná šikmosť), log-transformácia cieľa môže pomôcť.

# zabezpečenie, aby bolo všetko kladné (ak by boli nuly, pripočítame malú konštantu)
eps <- 1e-6
dat$TotalCost_pos <- ifelse(dat$TotalCost <= 0, dat$TotalCost + abs(min(dat$TotalCost)) + eps, dat$TotalCost)

model2 <- lm(log(TotalCost_pos) ~ 1 + Duration..days. + Traveler.age, data = dat)
summary(model2)

Call:
lm(formula = log(TotalCost_pos) ~ 1 + Duration..days. + Traveler.age, 
    data = dat)

Residuals:
     Min       1Q   Median       3Q      Max 
-1.95511 -0.36180 -0.03388  0.27143  2.04096 

Coefficients:
                 Estimate Std. Error t value Pr(>|t|)    
(Intercept)      7.024460   0.418866  16.770   <2e-16 ***
Duration..days. -0.017535   0.036842  -0.476    0.635    
Traveler.age     0.011721   0.008256   1.420    0.158    
---
Signif. codes:  
0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 0.683 on 134 degrees of freedom
Multiple R-squared:  0.01788,   Adjusted R-squared:  0.003225 
F-statistic:  1.22 on 2 and 134 DF,  p-value: 0.2985
# Diagnostické grafy pre model2
par(mfrow = c(2, 2), mar = c(4,4,2,1))

# Residuals vs Fitted
plot(fitted(model2), residuals(model2), 
     pch = 19, col = "darkred",
     xlab = "Fitted values", ylab = "Residuals",
     main = "Residuals vs Fitted")
abline(h = 0, col = "gray40", lty = 2)

# Q-Q plot
qqnorm(rstandard(model2), pch = 19, col = "darkblue",
       main = "Q-Q Plot of Standardized Residuals")
qqline(rstandard(model2), col = "gray40", lty = 2)

# Scale-Location plot
plot(fitted(model2), sqrt(abs(rstandard(model2))),
     pch = 19, col = "darkgreen",
     xlab = "Fitted values", ylab = expression(sqrt("|Standardized residuals|")),
     main = "Scale-Location")
abline(h = 0, col = "gray40", lty = 2)

# Residuals vs Leverage
plot(hatvalues(model2), rstandard(model2),
     pch = 19, col = "purple",
     xlab = "Leverage", ylab = "Standardized residuals",
     main = "Residuals vs Leverage")
abline(h = 0, col = "gray40", lty = 2)

par(mfrow = c(1, 1))



# Testy pre model2
res2 <- residuals(model2)
jb2 <- jarque.bera.test(res2)
jb2

    Jarque Bera Test

data:  res2
X-squared = 18.827, df = 2, p-value = 8.16e-05
out2 <- outlierTest(model2)
out2
No Studentized residuals with Bonferroni p < 0.05
Largest |rstudent|:

Interpretácia

Záver

LS0tCnRpdGxlOiAiRWNvbm9tZXRyaWNzIGluIFIg4oCTIGRvbcOhY2Egw7psb2hhIgpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sKYXV0aG9yOiAiQmFyYm9yYSBDYXBla292w6EiCi0tLQoKQnVkZW1lIG9kaGFkb3ZhxaUgbW9kZWwgKipjZWxrb3bDvWNoIGNlc3Rvdm7DvWNoIG7DoWtsYWRvdioqIHYgesOhdmlzbG9zdGkgb2QgKipkxLrFvmt5IHBvYnl0dSoqIGEgKip2ZWt1IGNlc3R1asO6Y2VqIG9zb2J5Kio6CgpcWwpcdGV4dGJme1RvdGFsQ29zdH0gPSBcdGV4dHtBY2NvbW1vZGF0aW9uIGNvc3R9ICsgXHRleHR7VHJhbnNwb3J0YXRpb24gY29zdH0KXF0KClxbClx0ZXh0e1RvdGFsQ29zdH0gPSBcYmV0YV8wICsgXGJldGFfMSBcY2RvdCBcdGV4dHtEdXJhdGlvbiAoZGF5cyl9ICsgXGJldGFfMiBcY2RvdCBcdGV4dHtUcmF2ZWxlciBhZ2V9ICsgdQpcXQoKIyMgS25pxb5uaWNlIGEgbmFzdGF2ZW5pZSBwcm9zdHJlZGlhCgpgYGB7cn0KbGlicmFyeSh6b28pCmxpYnJhcnkodHNlcmllcykKbGlicmFyeShsbXRlc3QpCmxpYnJhcnkoc2FuZHdpY2gpCmxpYnJhcnkoY2FyKQpybShsaXN0ID0gbHMoKSkKYGBgCgojIyBOYcSNw610YW5pZSBhIHByw61wcmF2YSBkYXRhYsOhenkKCmBgYHtyfQojIG5hxI3DrXRhbmllIGTDoXQgCmRhdGFfcmF3IDwtIHJlYWQuY3N2KCJUcmF2ZWxfZGF0YS5jc3YiLCBzZXAgPSAiOyIsIGhlYWRlciA9IFRSVUUsIHN0cmluZ3NBc0ZhY3RvcnMgPSBGQUxTRSkKCiMgcsO9Y2hsYSBrb250cm9sYSDFoXRydWt0w7pyeQpzdHIoZGF0YV9yYXcpCmhlYWQoZGF0YV9yYXcsIDUpCmBgYAoKIyMjIFR2b3JiYSBhbmFseXRpY2tlaiB0YWJ1xL5reQoKLSBWeXR2b3LDrW1lICoqVG90YWxDb3N0KiogPSBBY2NvbW1vZGF0aW9uIGNvc3QgKyBUcmFuc3BvcnRhdGlvbiBjb3N0Ci0gUHJlIGlzdG90dSBwcmVtZW51amVtZSBzdMS6cGNlIG5hIHN5bnRha3RpY2t5IGJlenBlxI1uw6kgbsOhenZ5Ci0gVnliZXJpZW1lIGxlbiBwb3RyZWJuw6kgcHJlbWVubsOpIGRvIG1vZGVsdQotIFVyb2LDrW1lICoqaW1wdXTDoWNpdSBtZWRpw6Fub20qKiBwcmUgY2jDvWJhasO6Y2UgaG9kbm90eSAoYWsgc2Egdnlza3l0bsO6KQoKYGBge3J9CiMgYmV6cGXEjW7DqSBuw6F6dnkgc3TEunBjb3YKbmFtZXMoZGF0YV9yYXcpIDwtIG1ha2UubmFtZXMobmFtZXMoZGF0YV9yYXcpKQoKIyBrb252ZXJ6aWUgxI3DrXNlbG7DvWNoIHByZW1lbm7DvWNoIChhayBieSBwcmkgaW1wb3J0ZSBvc3RhbGkgYWtvIHRleHQpCm51bV9jb2xzIDwtIGMoIkR1cmF0aW9uLi5kYXlzLiIsICJUcmF2ZWxlci5hZ2UiLCAiQWNjb21tb2RhdGlvbi5jb3N0IiwgIlRyYW5zcG9ydGF0aW9uLmNvc3QiKQpmb3IgKGNsIGluIG51bV9jb2xzKSB7CiAgaWYgKGNsICVpbiUgbmFtZXMoZGF0YV9yYXcpKSB7CiAgICBkYXRhX3Jhd1tbY2xdXSA8LSBzdXBwcmVzc1dhcm5pbmdzKGFzLm51bWVyaWMoZGF0YV9yYXdbW2NsXV0pKQogIH0KfQoKIyB2w71wb8SNZXQgVG90YWxDb3N0CmRhdGFfcmF3JFRvdGFsQ29zdCA8LSB3aXRoKGRhdGFfcmF3LCBBY2NvbW1vZGF0aW9uLmNvc3QgKyBUcmFuc3BvcnRhdGlvbi5jb3N0KQoKIyBwcmFjb3Zuw6EgdGFidcS+a2EgbGVuIHMgcG90cmVibsO9bWkgcHJlbWVubsO9bWkKZGF0IDwtIHN1YnNldChkYXRhX3Jhdywgc2VsZWN0ID0gYyhUb3RhbENvc3QsIER1cmF0aW9uLi5kYXlzLiwgVHJhdmVsZXIuYWdlKSkKCiMgaW1wdXTDoWNpYSBtZWRpw6Fub20gcHJlIE5BCmNvbF9tZWRzIDwtIHNhcHBseShkYXQsIGZ1bmN0aW9uKHgpIGlmIChpcy5udW1lcmljKHgpKSBtZWRpYW4oeCwgbmEucm0gPSBUUlVFKSBlbHNlIE5BKQpmb3IgKGNsIGluIG5hbWVzKGRhdCkpIHsKICBpZiAoaXMubnVtZXJpYyhkYXRbW2NsXV0pKSB7CiAgICBuYXMgPC0gaXMubmEoZGF0W1tjbF1dKQogICAgaWYgKGFueShuYXMpKSBkYXRbW2NsXV1bbmFzXSA8LSBjb2xfbWVkc1tjbF0KICB9Cn0KCnN1bW1hcnkoZGF0KQpgYGAKCiMjIyBSw71jaGxhIHZpenVhbGl6w6FjaWEg4oCTIGJveHBsb3R5CgpgYGB7cn0KcGFyKG1mcm93ID0gYygxLCAzKSwgbWFyID0gYyg0LCA0LCAyLCAxKSkKYm94cGxvdChkYXQkVG90YWxDb3N0LCBtYWluID0gIlRvdGFsQ29zdCIsIHhsYWIgPSAiIiwgY29sID0gImxpZ2h0Ymx1ZSIpCmJveHBsb3QoZGF0JER1cmF0aW9uLi5kYXlzLiwgbWFpbiA9ICJEdXJhdGlvbiAoZGF5cykiLCB4bGFiID0gIiIsIGNvbCA9ICJsaWdodGdyZWVuIikKYm94cGxvdChkYXQkVHJhdmVsZXIuYWdlLCBtYWluID0gIlRyYXZlbGVyIGFnZSIsIHhsYWIgPSAiIiwgY29sID0gImxpZ2h0cGluayIpCnBhcihtZnJvdyA9IGMoMSwgMSkpCmBgYAoKIyMgTGluZcOhcm5hIHJlZ3Jlc2lhIChNb2RlbCAxKQoKYGBge3J9Cm1vZGVsMSA8LSBsbShUb3RhbENvc3QgfiAxICsgRHVyYXRpb24uLmRheXMuICsgVHJhdmVsZXIuYWdlLCBkYXRhID0gZGF0KQpzdW1tYXJ5KG1vZGVsMSkKYGBgCgojIyMgRGlhZ25vc3RpY2vDqSBncmFmeQoKYGBge3J9CnBhcihtZnJvdyA9IGMoMiwgMiksIG1hciA9IGMoNCw0LDIsMSkpCgojIFJlc2lkdWFscyB2cyBGaXR0ZWQKcGxvdChmaXR0ZWQobW9kZWwxKSwgcmVzaWR1YWxzKG1vZGVsMSksIAogICAgIHBjaCA9IDE5LCBjb2wgPSAiZGFya3JlZCIsCiAgICAgeGxhYiA9ICJGaXR0ZWQgdmFsdWVzIiwgeWxhYiA9ICJSZXNpZHVhbHMiLAogICAgIG1haW4gPSAiUmVzaWR1YWxzIHZzIEZpdHRlZCIpCmFibGluZShoID0gMCwgY29sID0gImdyYXk0MCIsIGx0eSA9IDIpCgojIFEtUSBwbG90CnFxbm9ybShyc3RhbmRhcmQobW9kZWwxKSwgcGNoID0gMTksIGNvbCA9ICJkYXJrYmx1ZSIsCiAgICAgICBtYWluID0gIlEtUSBQbG90IG9mIFN0YW5kYXJkaXplZCBSZXNpZHVhbHMiKQpxcWxpbmUocnN0YW5kYXJkKG1vZGVsMSksIGNvbCA9ICJncmF5NDAiLCBsdHkgPSAyKQoKIyBTY2FsZS1Mb2NhdGlvbiBwbG90CnBsb3QoZml0dGVkKG1vZGVsMSksIHNxcnQoYWJzKHJzdGFuZGFyZChtb2RlbDEpKSksCiAgICAgcGNoID0gMTksIGNvbCA9ICJkYXJrZ3JlZW4iLAogICAgIHhsYWIgPSAiRml0dGVkIHZhbHVlcyIsIHlsYWIgPSBleHByZXNzaW9uKHNxcnQoInxTdGFuZGFyZGl6ZWQgcmVzaWR1YWxzfCIpKSwKICAgICBtYWluID0gIlNjYWxlLUxvY2F0aW9uIikKYWJsaW5lKGggPSAwLCBjb2wgPSAiZ3JheTQwIiwgbHR5ID0gMikKCiMgUmVzaWR1YWxzIHZzIExldmVyYWdlCnBsb3QoaGF0dmFsdWVzKG1vZGVsMSksIHJzdGFuZGFyZChtb2RlbDEpLAogICAgIHBjaCA9IDE5LCBjb2wgPSAicHVycGxlIiwKICAgICB4bGFiID0gIkxldmVyYWdlIiwgeWxhYiA9ICJTdGFuZGFyZGl6ZWQgcmVzaWR1YWxzIiwKICAgICBtYWluID0gIlJlc2lkdWFscyB2cyBMZXZlcmFnZSIpCmFibGluZShoID0gMCwgY29sID0gImdyYXk0MCIsIGx0eSA9IDIpCgpwYXIobWZyb3cgPSBjKDEsIDEpKQpgYGAKCiMjIyBUZXN0eSBub3JtYWxpdHkgYSBvZMS+YWhsb3N0w60KCmBgYHtyfQpyZXMxIDwtIHJlc2lkdWFscyhtb2RlbDEpCgojIEphcnF1ZeKAk0JlcmEgdGVzdCBub3JtYWxpdHkKamIxIDwtIGphcnF1ZS5iZXJhLnRlc3QocmVzMSkKamIxCgojIE91dGxpZXIgdGVzdCAoQm9uZmVycm9uaSkKb3V0MSA8LSBvdXRsaWVyVGVzdChtb2RlbDEpCm91dDEKYGBgCgojIyBBbHRlcm5hdMOtdm55IG1vZGVsIHNvIHptaWVybmVuw61tIHZwbHl2dSBvZMS+YWhsw71jaCBob2Ruw7R0IChNb2RlbCAyKQoKQWsgc8O6IG7DoWtsYWR5IHBveml0w612bmUgYSBwcmF2ZGVwb2RvYm5lIHByYWhvdm8gcG9zdW51dMOpIGEgxaFpa23DqSAocHJhdm9zdHJhbm7DoSDFoWlrbW9zxaUpLCBsb2ctdHJhbnNmb3Jtw6FjaWEgY2llxL5hIG3DtMW+ZSBwb23DtGPFpS4KCmBgYHtyfQojIHphYmV6cGXEjWVuaWUsIGFieSBib2xvIHbFoWV0a28ga2xhZG7DqSAoYWsgYnkgYm9saSBudWx5LCBwcmlwb8SNw610YW1lIG1hbMO6IGtvbsWhdGFudHUpCmVwcyA8LSAxZS02CmRhdCRUb3RhbENvc3RfcG9zIDwtIGlmZWxzZShkYXQkVG90YWxDb3N0IDw9IDAsIGRhdCRUb3RhbENvc3QgKyBhYnMobWluKGRhdCRUb3RhbENvc3QpKSArIGVwcywgZGF0JFRvdGFsQ29zdCkKCm1vZGVsMiA8LSBsbShsb2coVG90YWxDb3N0X3BvcykgfiAxICsgRHVyYXRpb24uLmRheXMuICsgVHJhdmVsZXIuYWdlLCBkYXRhID0gZGF0KQpzdW1tYXJ5KG1vZGVsMikKCiMgRGlhZ25vc3RpY2vDqSBncmFmeSBwcmUgbW9kZWwyCnBhcihtZnJvdyA9IGMoMiwgMiksIG1hciA9IGMoNCw0LDIsMSkpCgojIFJlc2lkdWFscyB2cyBGaXR0ZWQKcGxvdChmaXR0ZWQobW9kZWwyKSwgcmVzaWR1YWxzKG1vZGVsMiksIAogICAgIHBjaCA9IDE5LCBjb2wgPSAiZGFya3JlZCIsCiAgICAgeGxhYiA9ICJGaXR0ZWQgdmFsdWVzIiwgeWxhYiA9ICJSZXNpZHVhbHMiLAogICAgIG1haW4gPSAiUmVzaWR1YWxzIHZzIEZpdHRlZCIpCmFibGluZShoID0gMCwgY29sID0gImdyYXk0MCIsIGx0eSA9IDIpCgojIFEtUSBwbG90CnFxbm9ybShyc3RhbmRhcmQobW9kZWwyKSwgcGNoID0gMTksIGNvbCA9ICJkYXJrYmx1ZSIsCiAgICAgICBtYWluID0gIlEtUSBQbG90IG9mIFN0YW5kYXJkaXplZCBSZXNpZHVhbHMiKQpxcWxpbmUocnN0YW5kYXJkKG1vZGVsMiksIGNvbCA9ICJncmF5NDAiLCBsdHkgPSAyKQoKIyBTY2FsZS1Mb2NhdGlvbiBwbG90CnBsb3QoZml0dGVkKG1vZGVsMiksIHNxcnQoYWJzKHJzdGFuZGFyZChtb2RlbDIpKSksCiAgICAgcGNoID0gMTksIGNvbCA9ICJkYXJrZ3JlZW4iLAogICAgIHhsYWIgPSAiRml0dGVkIHZhbHVlcyIsIHlsYWIgPSBleHByZXNzaW9uKHNxcnQoInxTdGFuZGFyZGl6ZWQgcmVzaWR1YWxzfCIpKSwKICAgICBtYWluID0gIlNjYWxlLUxvY2F0aW9uIikKYWJsaW5lKGggPSAwLCBjb2wgPSAiZ3JheTQwIiwgbHR5ID0gMikKCiMgUmVzaWR1YWxzIHZzIExldmVyYWdlCnBsb3QoaGF0dmFsdWVzKG1vZGVsMiksIHJzdGFuZGFyZChtb2RlbDIpLAogICAgIHBjaCA9IDE5LCBjb2wgPSAicHVycGxlIiwKICAgICB4bGFiID0gIkxldmVyYWdlIiwgeWxhYiA9ICJTdGFuZGFyZGl6ZWQgcmVzaWR1YWxzIiwKICAgICBtYWluID0gIlJlc2lkdWFscyB2cyBMZXZlcmFnZSIpCmFibGluZShoID0gMCwgY29sID0gImdyYXk0MCIsIGx0eSA9IDIpCgpwYXIobWZyb3cgPSBjKDEsIDEpKQoKCiMgVGVzdHkgcHJlIG1vZGVsMgpyZXMyIDwtIHJlc2lkdWFscyhtb2RlbDIpCmpiMiA8LSBqYXJxdWUuYmVyYS50ZXN0KHJlczIpCmpiMgpvdXQyIDwtIG91dGxpZXJUZXN0KG1vZGVsMikKb3V0MgpgYGAKCiMjIEludGVycHJldMOhY2lhCgotIE/EjWFrw6F2YW1lLCDFvmUgKirOsjEgcHJpIER1cmF0aW9uIChkYXlzKSoqIGJ1ZGUgKiprbGFkbsO9Kiog4oCTIGRsaMWhw60gcG9ieXQgenZ5xaF1amUgY2Vsa292w6kgbsOha2xhZHkuCi0gS29lZmljaWVudCBwcmkgKipUcmF2ZWxlciBhZ2UqKiBtw7TFvmUgYnnFpSDCsSDigJMgYWsgcyB2ZWtvbSBza8O0ciB2b2zDrW1lIGtvbWZvcnRuZWrFoWllIHVieXRvdmFuaWUvZG9wcmF2dSwgbcO0xb5lIGJ5xaUga2xhZG7DvTsgYWsgc8O6IG1sYWTFocOtIGNlc3R1asO6Y2kgc2vDtHIg4oCebG93LWNvc3TigJwsIG3DtMW+ZSB2eWNow6FkemHFpSB6w6Fwb3Juw70uCi0gUG9yb3ZuYWp0ZSAqKk1vZGVsIDEgdnMuIE1vZGVsIDIqKiDigJMgYWsgbG9nLXRyYW5zZm9ybcOhY2lhIHpsZXDFocOtIGRpYWdub3N0aWt1IChsZXDFocOtIFFRLXBsb3QsIG1lbsWhaWEgaGV0ZXJvc2tlZGFzdGljaXRhKSwgcHJlZmVydWp0ZSBNb2RlbCAyLgoKIyMgWsOhdmVyCgotIE5hIG5hxaFpY2ggZMOhdGFjaCBzbWUgdGVzdG92YWxpLCDFvmUgKipkxLrFvmthIHBvYnl0dSoqIGEgKip2ZWsgY2VzdHVqw7pjZWogb3NvYnkqKiDFoXRhdGlzdGlja3kgdsO9em5hbW5lIGZvcm11asO6ICoqY2Vsa292w6kgbsOha2xhZHkgY2VzdHkqKi4KLSBEaWFnbm9zdGlja8OpIGdyYWZ5IGEgdGVzdHksIG5ham3DpCAqKkphcnF1ZeKAk0JlcmEqKiBhICoqb3V0bGllclRlc3QqKiwgcG9tb2hsaSBwb3PDumRpxaUgdmhvZG5vc8WlIHByZWRwb2tsYWRvdiBhIHZwbHl2IG1vxb5uw71jaCBvZMS+YWhsw71jaCBob2Ruw7R0LgoK