knitr::opts_chunk$set(echo = TRUE)
library(zoo)
library(tseries)
library(lmtest)
library(sandwich)
library(car)
library(MASS)
rm(list = ls())

#Popis dát a cieľ ##V tejto časti budeme modelovať, ako sú celkové náklady na zájazd (TotalCost) ovplyvnené vybranými charakteristikami cesty – najmä dĺžkou pobytu (Duration) a vekom cestovateľa (Age). Vytvoríme si teda premennú TotalCost a potom budeme krok za krokom testovať, či je lineárna špecifikácia modelu vhodná, podobne ako pri príklade s očakávanou dĺžkou života.

#PRÍPRAVA ÚDAJOV

# PRÍPRAVA ÚDAJOV
# načítanie pôvodných dát (uprav cestu k súboru podľa seba)
udaje_raw <- read.csv("Travel_data.csv",
dec = ".",
sep = ";",
header = TRUE)

# pozri si štruktúru, aby si videla presné názvy stĺpcov
str(udaje_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 ...

#Výber relevantných stĺpcov

udaje <- udaje_raw[, c("Duration..days.",
"Traveler.age",
"Accommodation.cost",
"Transportation.cost")]

# premenovanie na jednoduchšie názvy
names(udaje) <- c("Duration", "Age", "Accommodation", "Transport")

# vytvorenie celkových nákladov na zájazd
udaje$TotalCost <- udaje$Accommodation + udaje$Transport

# rýchla kontrola
summary(udaje)
    Duration           Age        Accommodation 
 Min.   : 5.000   Min.   :20.00   Min.   : 100  
 1st Qu.: 7.000   1st Qu.:28.00   1st Qu.: 600  
 Median : 7.000   Median :31.00   Median : 900  
 Mean   : 7.606   Mean   :33.18   Mean   :1245  
 3rd Qu.: 8.000   3rd Qu.:38.00   3rd Qu.:1200  
 Max.   :14.000   Max.   :60.00   Max.   :8000  
                                                
   Transport        TotalCost    
 Min.   :  20.0   Min.   :  200  
 1st Qu.: 200.0   1st Qu.: 1000  
 Median : 550.0   Median : 1400  
 Mean   : 645.2   Mean   : 1899  
 3rd Qu.: 800.0   3rd Qu.: 1900  
 Max.   :3000.0   Max.   :10500  
 NA's   :1        NA's   :1      

#Identifikácia číselných premenných

numeric_cols <- sapply(udaje, is.numeric)

# výpočet mediánov pre číselné stĺpce
column_medians <- sapply(udaje[, numeric_cols], median, na.rm = TRUE)

# imputácia chýbajúcich hodnôt
udaje_imputed <- udaje
for (col in names(column_medians)) {
udaje_imputed[[col]][is.na(udaje_imputed[[col]])] <- column_medians[col]
}

udaje <- udaje_imputed
summary(udaje)
    Duration           Age        Accommodation 
 Min.   : 5.000   Min.   :20.00   Min.   : 100  
 1st Qu.: 7.000   1st Qu.:28.00   1st Qu.: 600  
 Median : 7.000   Median :31.00   Median : 900  
 Mean   : 7.606   Mean   :33.18   Mean   :1245  
 3rd Qu.: 8.000   3rd Qu.:38.00   3rd Qu.:1200  
 Max.   :14.000   Max.   :60.00   Max.   :8000  
   Transport        TotalCost    
 Min.   :  20.0   Min.   :  200  
 1st Qu.: 200.0   1st Qu.: 1000  
 Median : 550.0   Median : 1400  
 Mean   : 644.5   Mean   : 1895  
 3rd Qu.: 800.0   3rd Qu.: 1900  
 Max.   :3000.0   Max.   :10500  

#Základný lineárny model ##Ako základný model zvolíme: ##vysvetľovaná premenná: TotalCost (celkové náklady na zájazd), ##vysvetľujúce premenné: Duration (počet dní) a Age (vek cestovateľa).

# ZÁKLADNÁ REGRESIA
attach(udaje)
model <- lm(TotalCost ~ +1 + Duration + Age, data = udaje)
summary(model)

Call:
lm(formula = TotalCost ~ +1 + Duration + Age, data = udaje)

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

Coefficients:
            Estimate Std. Error t value Pr(>|t|)  
(Intercept) 2401.686   1123.981   2.137   0.0344 *
Duration    -103.226     98.862  -1.044   0.2983  
Age            8.395     22.155   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

##Koeficient pri Duration ukazuje, o koľko sa v priemere zmenia celkové náklady na zájazd pri predĺžení pobytu o 1 deň (pri fixnom veku). Koeficient pri Age zachytáva, ako sa menia priemerné náklady s vekom cestovateľa (pri rovnakej dĺžke pobytu). R^2 a upravené R^2 hovoria, aká časť variability nákladov je vysvetlená týmito dvomi premennými.

#Test špecifikácie – Ramsey RESET test ##Najprv otestujeme, či lineárna špecifikácia modelu vyzerá byť vhodná alebo naznačuje, že chýbajú nejaké nelineárne členy (napr. štvorce, mocniny) alebo premenné. ##Formálne testujeme: ##H₀: model je správne špecifikovaný, ##H₁: model je nesprávne špecifikovaný.

resettest(model)

    RESET test

data:  model
RESET = 2.0638, df1 = 2, df2 = 132, p-value = 0.131

##Ak je p-hodnota < 0.05, zamietame H₀ → lineárna špecifikácia pravdepodobne nestačí,bolo by vhodné uvažovať o nelineárnej transformácii alebo doplnení premenných. ##Ak je p-hodnota ≥ 0.05, nemáme dôkaz o zlej funkčnej forme (aspoň podľa tohto testu).

#Grafická analýza špecifikácie ##Graf Residuals vs Fitted

plot(model, which = 1)

##Ak sú rezíduá rozptýlené náhodne okolo horizontálnej osi bez výrazného vzoru, lineárny model môže byť vhodný. ##Ak vidíš zakrivenie, lievikovitý tvar alebo systematický vzor, naznačuje to, že by mohla pomôcť transformácia (napr. log(TotalCost)) alebo pridanie nelineárnych členov (napr. Duration^2).

#C+R (Component + Residual) grafy

car::crPlots(model)

##Pre každú vysvetľujúcu premennú vidíš na osi X jej hodnoty a na osi Y kombináciu jej príspevku v modeli + rezíduá. ##Ak sa hladká krivka výrazne odchyľuje od priamky, pri danej premennej môže byť vhodné zvážiť napr. kvadratický člen (I(Duration^2)), logaritmus alebo inú transformáciu.

#Nelineárna špecifikácia – kvadratické členy ##Predpokladajme, že C+R graf naznačuje nelinearitu najmä pri Duration (napr. krátke vs. dlhé pobyty).

model_quad <- lm(TotalCost ~ +1 + Duration + I(Duration^2) + Age, data = udaje)
summary(model_quad)

Call:
lm(formula = TotalCost ~ +1 + Duration + I(Duration^2) + Age, 
    data = udaje)

Residuals:
    Min      1Q  Median      3Q     Max 
-1771.2  -883.7  -468.7    59.5  8608.2 

Coefficients:
              Estimate Std. Error t value Pr(>|t|)
(Intercept)    3161.74    2751.78   1.149    0.253
Duration       -292.97     634.42  -0.462    0.645
I(Duration^2)    11.32      37.38   0.303    0.763
Age               8.38      22.23   0.377    0.707

Residual standard error: 1839 on 133 degrees of freedom
Multiple R-squared:  0.01065,   Adjusted R-squared:  -0.01167 
F-statistic: 0.4771 on 3 and 133 DF,  p-value: 0.6987
anova(model, model_quad)
Analysis of Variance Table

Model 1: TotalCost ~ +1 + Duration + Age
Model 2: TotalCost ~ +1 + Duration + I(Duration^2) + Age
  Res.Df       RSS Df Sum of Sq      F Pr(>F)
1    134 450070864                           
2    133 449760797  1    310067 0.0917 0.7625
resettest(model_quad)

    RESET test

data:  model_quad
RESET = 2.6961, df1 = 2, df2 = 131, p-value = 0.07121

##Ak sa uprav. R² zvýši a zároveň Anova test ukáže, že nový model je štatisticky lepší (p-hodnota < 0.05), kvadratický člen Duration^2 pravdepodobne zlepšuje model. ##RESET test na kvadratickom modeli nám ukáže, či sme problém nesprávnej špecifikácie aspoň čiastočne odstránili.

#Rozšírený model (viac kvadratických členov)

model_rozsireny <- lm(TotalCost ~ Duration + I(Duration^2) + Age + I(Age^2),
data = udaje)
summary(model_rozsireny)

Call:
lm(formula = TotalCost ~ Duration + I(Duration^2) + Age + I(Age^2), 
    data = udaje)

Residuals:
    Min      1Q  Median      3Q     Max 
-1786.9  -894.7  -452.8   115.9  8627.0 

Coefficients:
               Estimate Std. Error t value Pr(>|t|)
(Intercept)   2242.4507  4269.9393   0.525    0.600
Duration      -273.1854   640.4787  -0.427    0.670
I(Duration^2)   10.2043    37.7185   0.271    0.787
Age             56.2824   171.1706   0.329    0.743
I(Age^2)        -0.6541     2.3173  -0.282    0.778

Residual standard error: 1845 on 132 degrees of freedom
Multiple R-squared:  0.01124,   Adjusted R-squared:  -0.01872 
F-statistic: 0.3753 on 4 and 132 DF,  p-value: 0.826
anova(model, model_rozsireny)
Analysis of Variance Table

Model 1: TotalCost ~ +1 + Duration + Age
Model 2: TotalCost ~ Duration + I(Duration^2) + Age + I(Age^2)
  Res.Df       RSS Df Sum of Sq      F Pr(>F)
1    134 450070864                           
2    132 449489502  2    581362 0.0854 0.9182
resettest(model_rozsireny)

    RESET test

data:  model_rozsireny
RESET = 2.1746, df1 = 2, df2 = 130, p-value = 0.1178

#Zlom pomocou dummy premennej – lineárna lomená funkcia ##Predpokladajme, že dlhšie pobyty (napr. od 9 dní vyššie) sa správajú inak ako kratšie pobyty. ##Zavedieme si dummy premennú DUM_long: ##DUM_long = 0, ak Duration < 9, ##DUM_long = 1, ak Duration ≥ 9.

udaje$DUM_long <- ifelse(udaje$Duration < 9, 0, 1)
table(udaje$DUM_long)

  0   1 
104  33 
#Zlom v autonómnom člene
modelD_auto <- lm(TotalCost ~ +1 + DUM_long + Duration + Age, data = udaje)
summary(modelD_auto)

Call:
lm(formula = TotalCost ~ +1 + DUM_long + Duration + Age, data = udaje)

Residuals:
    Min      1Q  Median      3Q     Max 
-1743.1  -827.2  -471.0   107.0  8576.0 

Coefficients:
            Estimate Std. Error t value Pr(>|t|)
(Intercept) 2052.678   1486.734   1.381    0.170
DUM_long    -223.505    620.489  -0.360    0.719
Duration     -54.615    167.483  -0.326    0.745
Age            9.393     22.399   0.419    0.676

Residual standard error: 1839 on 133 degrees of freedom
Multiple R-squared:  0.01093,   Adjusted R-squared:  -0.01138 
F-statistic: 0.4899 on 3 and 133 DF,  p-value: 0.6899

##Koeficient pri DUM_long ukazuje, o koľko sú v priemere celkové náklady iné pri dlhších pobytoch (Duration ≥ 9) oproti kratším (Duration < 9), pri rovnakej dĺžke a veku.

#Zlom v sklone – interakcia DUM_long * Duration

modelD_sklon <- lm(TotalCost ~ +1 + Duration + I(DUM_long * Duration) + Age,
data = udaje)
summary(modelD_sklon)

Call:
lm(formula = TotalCost ~ +1 + Duration + I(DUM_long * Duration) + 
    Age, data = udaje)

Residuals:
    Min      1Q  Median      3Q     Max 
-1731.3  -839.6  -462.0    99.8  8581.7 

Coefficients:
                       Estimate Std. Error t value Pr(>|t|)
(Intercept)            2131.267   1593.423   1.338    0.183
Duration                -65.350    186.261  -0.351    0.726
I(DUM_long * Duration)  -16.636     69.238  -0.240    0.810
Age                       9.056     22.403   0.404    0.687

Residual standard error: 1839 on 133 degrees of freedom
Multiple R-squared:  0.01039,   Adjusted R-squared:  -0.01193 
F-statistic: 0.4657 on 3 and 133 DF,  p-value: 0.7067
#Porovnanie modelov
anova(model, modelD_sklon)
Analysis of Variance Table

Model 1: TotalCost ~ +1 + Duration + Age
Model 2: TotalCost ~ +1 + Duration + I(DUM_long * Duration) + Age
  Res.Df       RSS Df Sum of Sq      F Pr(>F)
1    134 450070864                           
2    133 449875593  1    195271 0.0577 0.8105
resettest(modelD_sklon)

    RESET test

data:  modelD_sklon
RESET = 2.0851, df1 = 2, df2 = 131, p-value = 0.1284

##Sklon pri Duration pre krátke pobyty (DUM_long = 0) je daný koeficientom pri Duration. ##Pre dlhé pobyty (DUM_long = 1) je sklon = beta_Duration + beta_interakcia.

#Box-Coxova transformácia závislej premennej

boxcox(model)

##Z grafu Box-Coxovej transformácie zistíme optimálnu hodnotu λ (lambda), pri ktorej je log-vierohodnosť najvyššia. ##Približne: ##λ ≈ 1 → netreba transformovať, ##λ ≈ 0 → logaritmus: log(TotalCost), ##λ ≈ 0.5 → odmocnina: sqrt(TotalCost), ##λ ≈ -1 → recipročná transformácia: 1/TotalCost.

lambda_opt <- 0.5   
model_lambda <- lm(I((TotalCost^lambda_opt - 1) / lambda_opt) ~ Duration + Age,
data = udaje)
summary(model_lambda)

Call:
lm(formula = I((TotalCost^lambda_opt - 1)/lambda_opt) ~ Duration + 
    Age, data = udaje)

Residuals:
    Min      1Q  Median      3Q     Max 
-50.878 -16.598  -6.179   7.122 125.395 

Coefficients:
            Estimate Std. Error t value Pr(>|t|)    
(Intercept)  76.6500    19.5941   3.912 0.000145 ***
Duration     -1.3080     1.7234  -0.759 0.449205    
Age           0.3722     0.3862   0.964 0.336870    
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 31.95 on 134 degrees of freedom
Multiple R-squared:  0.01257,   Adjusted R-squared:  -0.002166 
F-statistic: 0.853 on 2 and 134 DF,  p-value: 0.4284
resettest(model_lambda)

    RESET test

data:  model_lambda
RESET = 0.59218, df1 = 2, df2 = 132, p-value = 0.5546

This is an R Markdown Notebook. When you execute code within the notebook, the results appear beneath the code.

Try executing this chunk by clicking the Run button within the chunk or by placing your cursor inside it and pressing Ctrl+Shift+Enter.

plot(cars)

Add a new chunk by clicking the Insert Chunk button on the toolbar or by pressing Ctrl+Alt+I.

When you save the notebook, an HTML file containing the code and output will be saved alongside it (click the Preview button or press Ctrl+Shift+K to preview the HTML file).

The preview shows you a rendered HTML copy of the contents of the editor. Consequently, unlike Knit, Preview does not run any R code chunks. Instead, the output of the chunk when it was last run in the editor is displayed.

LS0tCnRpdGxlOiAixaBwZWNpZmlrw6FjaWEgbW9kZWx1IOKAkyBjZXN0b3Zuw6kgbsOha2xhZHkiCm91dHB1dDogaHRtbF9ub3RlYm9vawphdXRob3I6ICJCYXJib3JhIENhcGVrb3bDoSIKLS0tCmBgYHtyfQprbml0cjo6b3B0c19jaHVuayRzZXQoZWNobyA9IFRSVUUpCmBgYAoKYGBge3J9CmxpYnJhcnkoem9vKQpsaWJyYXJ5KHRzZXJpZXMpCmxpYnJhcnkobG10ZXN0KQpsaWJyYXJ5KHNhbmR3aWNoKQpsaWJyYXJ5KGNhcikKbGlicmFyeShNQVNTKQpybShsaXN0ID0gbHMoKSkKYGBgCgojUG9waXMgZMOhdCBhIGNpZcS+CiMjViB0ZWp0byDEjWFzdGkgYnVkZW1lIG1vZGVsb3ZhxaUsIGFrbyBzw7ogY2Vsa292w6kgbsOha2xhZHkgbmEgesOhamF6ZCAoVG90YWxDb3N0KSBvdnBseXZuZW7DqSB2eWJyYW7DvW1pIGNoYXJha3RlcmlzdGlrYW1pIGNlc3R5IOKAkyBuYWptw6QgZMS6xb5rb3UgcG9ieXR1IChEdXJhdGlvbikgYSB2ZWtvbSBjZXN0b3ZhdGXEvmEgKEFnZSkuIFZ5dHZvcsOtbWUgc2kgdGVkYSBwcmVtZW5uw7ogVG90YWxDb3N0IGEgcG90b20gYnVkZW1lIGtyb2sgemEga3Jva29tIHRlc3RvdmHFpSwgxI1pIGplIGxpbmXDoXJuYSDFoXBlY2lmaWvDoWNpYSBtb2RlbHUgdmhvZG7DoSwgcG9kb2JuZSBha28gcHJpIHByw61rbGFkZSBzIG/EjWFrw6F2YW5vdSBkxLrFvmtvdSDFvml2b3RhLgoKI1BSw41QUkFWQSDDmkRBSk9WCmBgYHtyfQojIG5hxI3DrXRhbmllIHDDtHZvZG7DvWNoIGTDoXQgKHVwcmF2IGNlc3R1IGsgc8O6Ym9ydSBwb2TEvmEgc2ViYSkKdWRhamVfcmF3IDwtIHJlYWQuY3N2KCJUcmF2ZWxfZGF0YS5jc3YiLApkZWMgPSAiLiIsCnNlcCA9ICI7IiwKaGVhZGVyID0gVFJVRSkKCiMgcG96cmkgc2kgxaF0cnVrdMO6cnUsIGFieSBzaSB2aWRlbGEgcHJlc27DqSBuw6F6dnkgc3TEunBjb3YKc3RyKHVkYWplX3JhdykKYGBgCiNWw71iZXIgcmVsZXZhbnRuw71jaCBzdMS6cGNvdiAKYGBge3J9CnVkYWplIDwtIHVkYWplX3Jhd1ssIGMoIkR1cmF0aW9uLi5kYXlzLiIsCiJUcmF2ZWxlci5hZ2UiLAoiQWNjb21tb2RhdGlvbi5jb3N0IiwKIlRyYW5zcG9ydGF0aW9uLmNvc3QiKV0KCiMgcHJlbWVub3ZhbmllIG5hIGplZG5vZHVjaMWhaWUgbsOhenZ5Cm5hbWVzKHVkYWplKSA8LSBjKCJEdXJhdGlvbiIsICJBZ2UiLCAiQWNjb21tb2RhdGlvbiIsICJUcmFuc3BvcnQiKQoKIyB2eXR2b3JlbmllIGNlbGtvdsO9Y2ggbsOha2xhZG92IG5hIHrDoWphemQKdWRhamUkVG90YWxDb3N0IDwtIHVkYWplJEFjY29tbW9kYXRpb24gKyB1ZGFqZSRUcmFuc3BvcnQKCiMgcsO9Y2hsYSBrb250cm9sYQpzdW1tYXJ5KHVkYWplKQpgYGAKI0lkZW50aWZpa8OhY2lhIMSNw61zZWxuw71jaCBwcmVtZW5uw71jaApgYGB7cn0KbnVtZXJpY19jb2xzIDwtIHNhcHBseSh1ZGFqZSwgaXMubnVtZXJpYykKCiMgdsO9cG/EjWV0IG1lZGnDoW5vdiBwcmUgxI3DrXNlbG7DqSBzdMS6cGNlCmNvbHVtbl9tZWRpYW5zIDwtIHNhcHBseSh1ZGFqZVssIG51bWVyaWNfY29sc10sIG1lZGlhbiwgbmEucm0gPSBUUlVFKQoKIyBpbXB1dMOhY2lhIGNow71iYWrDumNpY2ggaG9kbsO0dAp1ZGFqZV9pbXB1dGVkIDwtIHVkYWplCmZvciAoY29sIGluIG5hbWVzKGNvbHVtbl9tZWRpYW5zKSkgewp1ZGFqZV9pbXB1dGVkW1tjb2xdXVtpcy5uYSh1ZGFqZV9pbXB1dGVkW1tjb2xdXSldIDwtIGNvbHVtbl9tZWRpYW5zW2NvbF0KfQoKdWRhamUgPC0gdWRhamVfaW1wdXRlZApzdW1tYXJ5KHVkYWplKQpgYGAKCiNaw6FrbGFkbsO9IGxpbmXDoXJueSBtb2RlbAojI0FrbyB6w6FrbGFkbsO9IG1vZGVsIHp2b2zDrW1lOgojI3Z5c3ZldMS+b3ZhbsOhIHByZW1lbm7DoTogVG90YWxDb3N0IChjZWxrb3bDqSBuw6FrbGFkeSBuYSB6w6FqYXpkKSwKIyN2eXN2ZXTEvnVqw7pjZSBwcmVtZW5uw6k6IER1cmF0aW9uIChwb8SNZXQgZG7DrSkgYSBBZ2UgKHZlayBjZXN0b3ZhdGXEvmEpLgpgYGB7cn0KIyBaw4FLTEFETsOBIFJFR1JFU0lBCmF0dGFjaCh1ZGFqZSkKbW9kZWwgPC0gbG0oVG90YWxDb3N0IH4gKzEgKyBEdXJhdGlvbiArIEFnZSwgZGF0YSA9IHVkYWplKQpzdW1tYXJ5KG1vZGVsKQpgYGAKIyNLb2VmaWNpZW50IHByaSBEdXJhdGlvbiB1a2F6dWplLCBvIGtvxL5rbyBzYSB2IHByaWVtZXJlIHptZW5pYSBjZWxrb3bDqSBuw6FrbGFkeSBuYSB6w6FqYXpkIHByaSBwcmVkxLrFvmVuw60gcG9ieXR1IG8gMSBkZcWIIChwcmkgZml4bm9tIHZla3UpLiBLb2VmaWNpZW50IHByaSBBZ2UgemFjaHl0w6F2YSwgYWtvIHNhIG1lbmlhIHByaWVtZXJuw6kgbsOha2xhZHkgcyB2ZWtvbSBjZXN0b3ZhdGXEvmEgKHByaSByb3ZuYWtlaiBkxLrFvmtlIHBvYnl0dSkuIFJeMiBhIHVwcmF2ZW7DqSBSXjIgaG92b3JpYSwgYWvDoSDEjWFzxaUgdmFyaWFiaWxpdHkgbsOha2xhZG92IGplIHZ5c3ZldGxlbsOhIHTDvW1pdG8gZHZvbWkgcHJlbWVubsO9bWkuCgojVGVzdCDFoXBlY2lmaWvDoWNpZSDigJMgUmFtc2V5IFJFU0VUIHRlc3QKIyNOYWpwcnYgb3Rlc3R1amVtZSwgxI1pIGxpbmXDoXJuYSDFoXBlY2lmaWvDoWNpYSBtb2RlbHUgdnl6ZXLDoSBiecWlIHZob2Ruw6EgYWxlYm8gbmF6bmHEjXVqZSwgxb5lIGNow71iYWrDuiBuZWpha8OpIG5lbGluZcOhcm5lIMSNbGVueSAobmFwci4gxaF0dm9yY2UsIG1vY25pbnkpIGFsZWJvIHByZW1lbm7DqS4KIyNGb3Jtw6FsbmUgdGVzdHVqZW1lOgojI0jigoA6IG1vZGVsIGplIHNwcsOhdm5lIMWhcGVjaWZpa292YW7DvSwKIyNI4oKBOiBtb2RlbCBqZSBuZXNwcsOhdm5lIMWhcGVjaWZpa292YW7DvS4KYGBge3J9CnJlc2V0dGVzdChtb2RlbCkKYGBgCiMjQWsgamUgcC1ob2Rub3RhIDwgMC4wNSwgemFtaWV0YW1lIEjigoAg4oaSIGxpbmXDoXJuYSDFoXBlY2lmaWvDoWNpYSBwcmF2ZGVwb2RvYm5lIG5lc3RhxI3DrSxib2xvIGJ5IHZob2Ruw6kgdXZhxb5vdmHFpSBvIG5lbGluZcOhcm5laiB0cmFuc2Zvcm3DoWNpaSBhbGVibyBkb3BsbmVuw60gcHJlbWVubsO9Y2guCiMjQWsgamUgcC1ob2Rub3RhIOKJpSAwLjA1LCBuZW3DoW1lIGTDtGtheiBvIHpsZWogZnVua8SNbmVqIGZvcm1lIChhc3BvxYggcG9kxL5hIHRvaHRvIHRlc3R1KS4KCiNHcmFmaWNrw6EgYW5hbMO9emEgxaFwZWNpZmlrw6FjaWUKIyNHcmFmIFJlc2lkdWFscyB2cyBGaXR0ZWQKYGBge3J9CnBsb3QobW9kZWwsIHdoaWNoID0gMSkKYGBgCiMjQWsgc8O6IHJlesOtZHXDoSByb3pwdMO9bGVuw6kgbsOhaG9kbmUgb2tvbG8gaG9yaXpvbnTDoWxuZWogb3NpIGJleiB2w71yYXpuw6lobyB2em9ydSwgbGluZcOhcm55IG1vZGVsIG3DtMW+ZSBiecWlIHZob2Ruw70uCiMjQWsgdmlkw63FoSB6YWtyaXZlbmllLCBsaWV2aWtvdml0w70gdHZhciBhbGVibyBzeXN0ZW1hdGlja8O9IHZ6b3IsIG5hem5hxI11amUgdG8sIMW+ZSBieSBtb2hsYSBwb23DtGPFpSB0cmFuc2Zvcm3DoWNpYSAobmFwci4gbG9nKFRvdGFsQ29zdCkpIGFsZWJvIHByaWRhbmllIG5lbGluZcOhcm55Y2ggxI1sZW5vdiAobmFwci4gRHVyYXRpb25eMikuCgojQytSIChDb21wb25lbnQgKyBSZXNpZHVhbCkgZ3JhZnkKYGBge3J9CmNhcjo6Y3JQbG90cyhtb2RlbCkKYGBgCiMjUHJlIGthxb5kw7ogdnlzdmV0xL51asO6Y3UgcHJlbWVubsO6IHZpZMOtxaEgbmEgb3NpIFggamVqIGhvZG5vdHkgYSBuYSBvc2kgWSBrb21iaW7DoWNpdSBqZWogcHLDrXNwZXZrdSB2IG1vZGVsaSArIHJlesOtZHXDoS4KIyNBayBzYSBobGFka8OhIGtyaXZrYSB2w71yYXpuZSBvZGNoecS+dWplIG9kIHByaWFta3ksIHByaSBkYW5laiBwcmVtZW5uZWogbcO0xb5lIGJ5xaUgdmhvZG7DqSB6dsOhxb5pxaUgbmFwci4ga3ZhZHJhdGlja8O9IMSNbGVuIChJKER1cmF0aW9uXjIpKSwgbG9nYXJpdG11cyBhbGVibyBpbsO6IHRyYW5zZm9ybcOhY2l1LgoKI05lbGluZcOhcm5hIMWhcGVjaWZpa8OhY2lhIOKAkyBrdmFkcmF0aWNrw6kgxI1sZW55CiMjUHJlZHBva2xhZGFqbWUsIMW+ZSBDK1IgZ3JhZiBuYXpuYcSNdWplIG5lbGluZWFyaXR1IG5ham3DpCBwcmkgRHVyYXRpb24gKG5hcHIuIGtyw6F0a2UgdnMuIGRsaMOpIHBvYnl0eSkuCmBgYHtyfQptb2RlbF9xdWFkIDwtIGxtKFRvdGFsQ29zdCB+ICsxICsgRHVyYXRpb24gKyBJKER1cmF0aW9uXjIpICsgQWdlLCBkYXRhID0gdWRhamUpCnN1bW1hcnkobW9kZWxfcXVhZCkKCmFub3ZhKG1vZGVsLCBtb2RlbF9xdWFkKQpyZXNldHRlc3QobW9kZWxfcXVhZCkKYGBgCiMjQWsgc2EgdXByYXYuIFLCsiB6dsO9xaFpIGEgesOhcm92ZcWIIEFub3ZhIHRlc3QgdWvDocW+ZSwgxb5lIG5vdsO9IG1vZGVsIGplIMWhdGF0aXN0aWNreSBsZXDFocOtIChwLWhvZG5vdGEgPCAwLjA1KSwga3ZhZHJhdGlja8O9IMSNbGVuIER1cmF0aW9uXjIgcHJhdmRlcG9kb2JuZSB6bGVwxaF1amUgbW9kZWwuCiMjUkVTRVQgdGVzdCBuYSBrdmFkcmF0aWNrb20gbW9kZWxpIG7DoW0gdWvDocW+ZSwgxI1pIHNtZSBwcm9ibMOpbSBuZXNwcsOhdm5laiDFoXBlY2lmaWvDoWNpZSBhc3BvxYggxI1pYXN0b8SNbmUgb2RzdHLDoW5pbGkuCgojUm96xaHDrXJlbsO9IG1vZGVsICh2aWFjIGt2YWRyYXRpY2vDvWNoIMSNbGVub3YpCmBgYHtyfQptb2RlbF9yb3pzaXJlbnkgPC0gbG0oVG90YWxDb3N0IH4gRHVyYXRpb24gKyBJKER1cmF0aW9uXjIpICsgQWdlICsgSShBZ2VeMiksCmRhdGEgPSB1ZGFqZSkKc3VtbWFyeShtb2RlbF9yb3pzaXJlbnkpCmFub3ZhKG1vZGVsLCBtb2RlbF9yb3pzaXJlbnkpCnJlc2V0dGVzdChtb2RlbF9yb3pzaXJlbnkpCmBgYAojWmxvbSBwb21vY291IGR1bW15IHByZW1lbm5laiDigJMgbGluZcOhcm5hIGxvbWVuw6EgZnVua2NpYQojI1ByZWRwb2tsYWRham1lLCDFvmUgZGxoxaFpZSBwb2J5dHkgKG5hcHIuIG9kIDkgZG7DrSB2ecWhxaFpZSkgc2Egc3Byw6F2YWrDuiBpbmFrIGFrbyBrcmF0xaFpZSBwb2J5dHkuCiMjWmF2ZWRpZW1lIHNpIGR1bW15IHByZW1lbm7DuiBEVU1fbG9uZzoKIyNEVU1fbG9uZyA9IDAsIGFrIER1cmF0aW9uIDwgOSwKIyNEVU1fbG9uZyA9IDEsIGFrIER1cmF0aW9uIOKJpSA5LgpgYGB7cn0KdWRhamUkRFVNX2xvbmcgPC0gaWZlbHNlKHVkYWplJER1cmF0aW9uIDwgOSwgMCwgMSkKdGFibGUodWRhamUkRFVNX2xvbmcpCgojWmxvbSB2IGF1dG9uw7Ntbm9tIMSNbGVuZQptb2RlbERfYXV0byA8LSBsbShUb3RhbENvc3QgfiArMSArIERVTV9sb25nICsgRHVyYXRpb24gKyBBZ2UsIGRhdGEgPSB1ZGFqZSkKc3VtbWFyeShtb2RlbERfYXV0bykKYGBgCiMjS29lZmljaWVudCBwcmkgRFVNX2xvbmcgdWthenVqZSwgbyBrb8S+a28gc8O6IHYgcHJpZW1lcmUgY2Vsa292w6kgbsOha2xhZHkgaW7DqSBwcmkgZGxoxaHDrWNoIHBvYnl0b2NoIChEdXJhdGlvbiDiiaUgOSkgb3Byb3RpIGtyYXTFocOtbSAoRHVyYXRpb24gPCA5KSwgcHJpIHJvdm5ha2VqIGTEusW+a2UgYSB2ZWt1LgoKI1psb20gdiBza2xvbmUg4oCTIGludGVyYWtjaWEgRFVNX2xvbmcgKiBEdXJhdGlvbgpgYGB7cn0KbW9kZWxEX3NrbG9uIDwtIGxtKFRvdGFsQ29zdCB+ICsxICsgRHVyYXRpb24gKyBJKERVTV9sb25nICogRHVyYXRpb24pICsgQWdlLApkYXRhID0gdWRhamUpCnN1bW1hcnkobW9kZWxEX3NrbG9uKQoKI1Bvcm92bmFuaWUgbW9kZWxvdgphbm92YShtb2RlbCwgbW9kZWxEX3NrbG9uKQpyZXNldHRlc3QobW9kZWxEX3NrbG9uKQpgYGAKIyNTa2xvbiBwcmkgRHVyYXRpb24gcHJlIGtyw6F0a2UgcG9ieXR5IChEVU1fbG9uZyA9IDApIGplIGRhbsO9IGtvZWZpY2llbnRvbSBwcmkgRHVyYXRpb24uCiMjUHJlIGRsaMOpIHBvYnl0eSAoRFVNX2xvbmcgPSAxKSBqZSBza2xvbiA9IGJldGFfRHVyYXRpb24gKyBiZXRhX2ludGVyYWtjaWEuCgojQm94LUNveG92YSB0cmFuc2Zvcm3DoWNpYSB6w6F2aXNsZWogcHJlbWVubmVqCmBgYHtyfQpib3hjb3gobW9kZWwpCmBgYAojI1ogZ3JhZnUgQm94LUNveG92ZWogdHJhbnNmb3Jtw6FjaWUgemlzdMOtbWUgb3B0aW3DoWxudSBob2Rub3R1IM67IChsYW1iZGEpLCBwcmkga3RvcmVqIGplIGxvZy12aWVyb2hvZG5vc8WlIG5hanZ5xaHFoWlhLgojI1ByaWJsacW+bmU6CiMjzrsg4omIIDEg4oaSIG5ldHJlYmEgdHJhbnNmb3Jtb3ZhxaUsCiMjzrsg4omIIDAg4oaSIGxvZ2FyaXRtdXM6IGxvZyhUb3RhbENvc3QpLAojI867IOKJiCAwLjUg4oaSIG9kbW9jbmluYTogc3FydChUb3RhbENvc3QpLAojI867IOKJiCAtMSDihpIgcmVjaXByb8SNbsOhIHRyYW5zZm9ybcOhY2lhOiAxL1RvdGFsQ29zdC4KYGBge3J9CmxhbWJkYV9vcHQgPC0gMC41ICAgCm1vZGVsX2xhbWJkYSA8LSBsbShJKChUb3RhbENvc3RebGFtYmRhX29wdCAtIDEpIC8gbGFtYmRhX29wdCkgfiBEdXJhdGlvbiArIEFnZSwKZGF0YSA9IHVkYWplKQpzdW1tYXJ5KG1vZGVsX2xhbWJkYSkKcmVzZXR0ZXN0KG1vZGVsX2xhbWJkYSkKYGBgCgoKVGhpcyBpcyBhbiBbUiBNYXJrZG93bl0oaHR0cDovL3JtYXJrZG93bi5yc3R1ZGlvLmNvbSkgTm90ZWJvb2suIFdoZW4geW91IGV4ZWN1dGUgY29kZSB3aXRoaW4gdGhlIG5vdGVib29rLCB0aGUgcmVzdWx0cyBhcHBlYXIgYmVuZWF0aCB0aGUgY29kZS4gCgpUcnkgZXhlY3V0aW5nIHRoaXMgY2h1bmsgYnkgY2xpY2tpbmcgdGhlICpSdW4qIGJ1dHRvbiB3aXRoaW4gdGhlIGNodW5rIG9yIGJ5IHBsYWNpbmcgeW91ciBjdXJzb3IgaW5zaWRlIGl0IGFuZCBwcmVzc2luZyAqQ3RybCtTaGlmdCtFbnRlciouIAoKYGBge3J9CnBsb3QoY2FycykKYGBgCgpBZGQgYSBuZXcgY2h1bmsgYnkgY2xpY2tpbmcgdGhlICpJbnNlcnQgQ2h1bmsqIGJ1dHRvbiBvbiB0aGUgdG9vbGJhciBvciBieSBwcmVzc2luZyAqQ3RybCtBbHQrSSouCgpXaGVuIHlvdSBzYXZlIHRoZSBub3RlYm9vaywgYW4gSFRNTCBmaWxlIGNvbnRhaW5pbmcgdGhlIGNvZGUgYW5kIG91dHB1dCB3aWxsIGJlIHNhdmVkIGFsb25nc2lkZSBpdCAoY2xpY2sgdGhlICpQcmV2aWV3KiBidXR0b24gb3IgcHJlc3MgKkN0cmwrU2hpZnQrSyogdG8gcHJldmlldyB0aGUgSFRNTCBmaWxlKS4KClRoZSBwcmV2aWV3IHNob3dzIHlvdSBhIHJlbmRlcmVkIEhUTUwgY29weSBvZiB0aGUgY29udGVudHMgb2YgdGhlIGVkaXRvci4gQ29uc2VxdWVudGx5LCB1bmxpa2UgKktuaXQqLCAqUHJldmlldyogZG9lcyBub3QgcnVuIGFueSBSIGNvZGUgY2h1bmtzLiBJbnN0ZWFkLCB0aGUgb3V0cHV0IG9mIHRoZSBjaHVuayB3aGVuIGl0IHdhcyBsYXN0IHJ1biBpbiB0aGUgZWRpdG9yIGlzIGRpc3BsYXllZC4K