Úvod

Tento dokument analyzuje dataset DailyDelhiClimateTest.csv, ktorý obsahuje dennú klímu v Dillí. Dáta obsahujú 114 pozorovaní a tieto premenné:

  • date — dátum merania
  • meantemp — priemerná denná teplota (°C)
  • humidity — priemerná relatívna vlhkosť (%)
  • wind_speed — priemerná rýchlosť vetra
  • meanpressure — priemerný atmosférický tlak

Cieľom analýzy je overiť špecifikáciu modelu pomocou Ramsey RESET testu, preskúmať možné nelinearity a transformácie, a porovnať alternatívne modely (lineárny, kvadratický, a model s lomom podľa rýchlosti vetra).

Načítanie dát

df <- read.csv("DailyDelhiClimateTest.csv")
summary(df)
     date              meantemp        humidity       wind_speed      meanpressure 
 Length:114         Min.   :11.00   Min.   :17.75   Min.   : 1.387   Min.   :  59  
 Class :character   1st Qu.:16.44   1st Qu.:39.62   1st Qu.: 5.564   1st Qu.:1007  
 Mode  :character   Median :19.88   Median :57.75   Median : 8.069   Median :1013  
                    Mean   :21.71   Mean   :56.26   Mean   : 8.144   Mean   :1004  
                    3rd Qu.:27.71   3rd Qu.:71.90   3rd Qu.:10.069   3rd Qu.:1017  
                    Max.   :34.50   Max.   :95.83   Max.   :19.314   Max.   :1023  
glimpse(df)
Rows: 114
Columns: 5
$ date         <chr> "2017-01-01", "2017-01-02", "2017-01-03", "2017-01-04", "2017-01-05", "2017-01-06", …
$ meantemp     <dbl> 15.91304, 18.50000, 17.11111, 18.70000, 18.38889, 19.31818, 14.70833, 15.68421, 14.5…
$ humidity     <dbl> 85.86957, 77.22222, 81.88889, 70.05000, 74.94444, 79.31818, 95.83333, 83.52632, 80.8…
$ wind_speed   <dbl> 2.743478, 2.894444, 4.016667, 4.545000, 3.300000, 8.681818, 10.041667, 1.950000, 6.5…
$ meanpressure <dbl> 59.000, 1018.278, 1018.333, 1015.700, 1014.333, 1011.773, 1011.375, 1015.550, 1015.9…

Základný lineárny model

m1 <- lm(meantemp ~ humidity + wind_speed + meanpressure, data = df)
summary(m1)

Call:
lm(formula = meantemp ~ humidity + wind_speed + meanpressure, 
    data = df)

Residuals:
    Min      1Q  Median      3Q     Max 
-6.4346 -2.3787  0.0088  2.3849  7.3508 

Coefficients:
              Estimate Std. Error t value Pr(>|t|)    
(Intercept)  42.823718   3.741722  11.445   <2e-16 ***
humidity     -0.296567   0.017141 -17.302   <2e-16 ***
wind_speed   -0.140056   0.091436  -1.532    0.128    
meanpressure -0.003272   0.003464  -0.945    0.347    
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 3.261 on 110 degrees of freedom
Multiple R-squared:  0.744, Adjusted R-squared:  0.737 
F-statistic: 106.6 on 3 and 110 DF,  p-value: < 2.2e-16

Výsledky lineárnej regresie skúmajúce vplyv vlhkosti, rýchlosti vetra a priemerného tlaku na priemernú teplotu ukazujú, že model dokáže vysvetliť približne 74,4 % variability teploty, čo je veľmi dobré. Priemerná odchýlka predpovedí od skutočných hodnôt je približne 3,26 °C a medián reziduálov je takmer nulový, čo naznačuje, že model je vo väčšine prípadov presný. Jedinou premennou so štatisticky významným vplyvom je vlhkosť, ktorá má negatívny efekt – s každým zvýšením vlhkosti o jednu jednotku klesá teplota približne o 0,3 °C. Rýchlosť vetra a priemerný tlak sa ukázali ako nevýznamné, ich vplyv na teplotu nie je štatisticky preukázaný. Intercept modelu udáva predpokladanú priemernú teplotu, keď sú všetky tri premenné nulové, a je približne 42,8 °C. Celkovo je model štatisticky významný, čo potvrdzuje vysoká hodnota F-štatistiky. Reziduály naznačujú, že niektoré predpovede sa odchyľujú až o 6–7 stupňov od skutočných hodnôt, no väčšina odchýlok je menšia. Z výsledkov vyplýva, že vlhkosť je kľúčovým faktorom ovplyvňujúcim priemernú teplotu v analyzovaných dátach, zatiaľ čo ostatné premenné majú zanedbateľný efekt. Model teda poskytuje spoľahlivý náhľad na závislosť teploty od meteorologických faktorov, pričom jeho predpovede sú vo všeobecnosti presné a vysvetľujú väčšinu variability dát.

Ramsey RESET test

resettest(m1, power = 2, type = "fitted")

    RESET test

data:  m1
RESET = 7.4471, df1 = 1, df2 = 109, p-value = 0.007409

Výsledky RESET testu (Regression Equation Specification Error Test) pre model m1 ukazujú, že hodnota testovej štatistiky je 7,4471 s 1 a 109 stupňami voľnosti a p-hodnota je 0,0074. To naznačuje, že model môže byť nesprávne špecifikovaný, teda lineárna forma modelu nemusí úplne zachytávať vzťah medzi premennými. P-hodnota nižšia ako 0,05 znamená, že existuje štatisticky významný dôkaz proti nulovej hypotéze, podľa ktorej je model správne špecifikovaný. Inými slovami, model pravdepodobne vynecháva nejaké nelineárne vzťahy alebo interakcie medzi premennými, ktoré by mohli zlepšiť jeho presnosť.

Diagnostické grafy

par(mfrow = c(1,1))
plot(m1$fitted.values, resid(m1), main="Residuals vs Fitted (baseline)", xlab="Fitted values", ylab="Residuals")
abline(h=0, col="red", lty=2)

Diagnostický graf “Zvyšky vs. Spočítané hodnoty” slúži na overenie kľúčových predpokladov lineárnej regresie. Na grafe vidíme náhodný a rovnomerný rozptyl bodov okolo červenej nulovej línie (\(y=0\)). To je ideálny stav.Tento rozptyl bez viditeľného systémového vzoru (ako lievik alebo krivka) silne naznačuje, že:Bol splnený predpoklad homoskedasticity (konštantný rozptyl chýb).Vzťah medzi premennými je pravdepodobne lineárny.Model \(m1\) je teda primerane vhodný a spĺňa základné predpoklady o rozptyle. V grafe sa nachádza len niekoľko extrémnych hodnôt (outliers), ktoré by bolo vhodné dodatočne preskúmať.

car::crPlots(m1)

Tieto grafy porovnávajú lineárny vzťah modelu (modrá čiara) so skutočným tvarom vzťahu v dátach (ružová čiara).Humidity (Vlhkosť): Modrá a ružová čiara sú si veľmi blízke. Lineárny vzťah bol správne špecifikovaný.Wind_speed (Rýchlosť vetra): Čiary sú taktiež blízke. Lineárny predpoklad je v poriadku. Meanpressure (Priemerný tlak): Ružová čiara sa dramaticky odchyľuje od modrej. To je jasný dôkaz, že predpoklad linearity bol porušený.

Kvadratický model

m2 <- lm(meantemp ~ humidity + wind_speed + meanpressure +
            I(humidity^2) + I(wind_speed^2) + I(meanpressure^2), data = df)
summary(m2)

Call:
lm(formula = meantemp ~ humidity + wind_speed + meanpressure + 
    I(humidity^2) + I(wind_speed^2) + I(meanpressure^2), data = df)

Residuals:
    Min      1Q  Median      3Q     Max 
-5.7992 -1.9583  0.0246  2.0645  5.1344 

Coefficients:
                    Estimate Std. Error t value Pr(>|t|)    
(Intercept)       -4.565e+00  7.171e+00  -0.637   0.5257    
humidity          -2.309e-01  9.291e-02  -2.485   0.0145 *  
wind_speed        -2.442e-01  2.609e-01  -0.936   0.3514    
meanpressure       6.469e-01  8.041e-02   8.044 1.26e-12 ***
I(humidity^2)      6.561e-04  7.759e-04   0.846   0.3997    
I(wind_speed^2)    8.862e-03  1.429e-02   0.620   0.5365    
I(meanpressure^2) -6.016e-04  7.449e-05  -8.076 1.08e-12 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 2.532 on 107 degrees of freedom
Multiple R-squared:  0.8499,    Adjusted R-squared:  0.8415 
F-statistic:   101 on 6 and 107 DF,  p-value: < 2.2e-16
anova(m1, m2)
Analysis of Variance Table

Model 1: meantemp ~ humidity + wind_speed + meanpressure
Model 2: meantemp ~ humidity + wind_speed + meanpressure + I(humidity^2) + 
    I(wind_speed^2) + I(meanpressure^2)
  Res.Df     RSS Df Sum of Sq      F    Pr(>F)    
1    110 1170.07                                  
2    107  685.87  3     484.2 25.179 2.105e-12 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
resettest(m2, power=2, type="fitted")

    RESET test

data:  m2
RESET = 8.5516, df1 = 1, df2 = 106, p-value = 0.004222

Do modelu boli pridané kvadratické členy premenných, aby sa zachytili nelineárne vzťahy s priemernou teplotou. Reziduály sa zmenšili, priemerne sa odchyľujú o 2,53 °C, čo naznačuje lepšie prispôsobenie dátam. Významný vplyv na teplotu majú vlhkosť, priemerný tlak a jeho kvadratický člen, ostatné členy nie sú štatisticky významné. Model vysvetľuje približne 84 % variability teploty a pridanie kvadratických členov významne zlepšilo jeho presnosť. RESET test však stále naznačuje, že model nemusí úplne zachytávať všetky nelineárne vzťahy (p = 0,0042). Celkovo nový model lepšie vystihuje závislosť teploty od meteorologických faktorov, no stále existuje priestor na zlepšenie.

Box–Cox transformácia cieľovej premennej

bc <- boxcox(m1, data = df)

(lambda <- bc$x[which.max(bc$y)])
[1] 0.3434343

Graf Box-Cox transformácie hľadá optimálnu hodnotu pre transformáciu závislej premennej. Optimálna hodnota: Vrchol krivky (maximum log-vierohodnosti) je približne pri lambde 0,34. Interval spoľahlivosti: 95% interval spoľahlivosti pre lamdu (ohraničený zvislými čiarami) zahŕňa hodnotu .

df$meantemp_bc <- (df$meantemp^lambda - 1)/lambda
m3 <- lm(meantemp_bc ~ humidity + wind_speed + meanpressure, data = df)
summary(m3)

Call:
lm(formula = meantemp_bc ~ humidity + wind_speed + meanpressure, 
    data = df)

Residuals:
     Min       1Q   Median       3Q      Max 
-1.01649 -0.34253  0.05839  0.34554  0.82117 

Coefficients:
               Estimate Std. Error t value Pr(>|t|)    
(Intercept)   8.1333300  0.4945401  16.446   <2e-16 ***
humidity     -0.0387929  0.0022655 -17.123   <2e-16 ***
wind_speed   -0.0180166  0.0120851  -1.491    0.139    
meanpressure -0.0004137  0.0004579  -0.904    0.368    
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 0.4311 on 110 degrees of freedom
Multiple R-squared:  0.7402,    Adjusted R-squared:  0.7331 
F-statistic: 104.5 on 3 and 110 DF,  p-value: < 2.2e-16
resettest(m3, power=2, type="fitted")

    RESET test

data:  m3
RESET = 3.6061, df1 = 1, df2 = 109, p-value = 0.06021

Model s transformovanou teplotou (meantemp_bc) veľmi dobre predpovedá hodnoty, reziduály sú malé (±0,43). Významný vplyv na teplotu má iba vlhkosť, ktorá má negatívny efekt, zatiaľ čo rýchlosť vetra a priemerný tlak nie sú významné. Model vysvetľuje približne 74 % variability teploty a je štatisticky významný. RESET test (p = 0,060) nenaznačuje výraznú nesprávnu špecifikáciu modelu, takže lineárna forma s transformovanou premennou je vhodná.

Piecewise (lomový) model podľa wind_speed > 10

df <- df %>% mutate(D_wind_high = ifelse(wind_speed > 10, 1, 0))
m4 <- lm(meantemp ~ humidity + wind_speed + D_wind_high + I(D_wind_high*wind_speed) + meanpressure, data=df)
summary(m4)

Call:
lm(formula = meantemp ~ humidity + wind_speed + D_wind_high + 
    I(D_wind_high * wind_speed) + meanpressure, data = df)

Residuals:
    Min      1Q  Median      3Q     Max 
-6.3345 -2.2007  0.0068  2.4501  7.3959 

Coefficients:
                             Estimate Std. Error t value Pr(>|t|)    
(Intercept)                 43.309586   3.759192  11.521   <2e-16 ***
humidity                    -0.301661   0.017450 -17.287   <2e-16 ***
wind_speed                  -0.164822   0.159300  -1.035    0.303    
D_wind_high                  4.623241   3.352098   1.379    0.171    
I(D_wind_high * wind_speed) -0.320819   0.293371  -1.094    0.277    
meanpressure                -0.003430   0.003486  -0.984    0.327    
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 3.256 on 108 degrees of freedom
Multiple R-squared:  0.7495,    Adjusted R-squared:  0.7379 
F-statistic: 64.63 on 5 and 108 DF,  p-value: < 2.2e-16
anova(m1, m4)
Analysis of Variance Table

Model 1: meantemp ~ humidity + wind_speed + meanpressure
Model 2: meantemp ~ humidity + wind_speed + D_wind_high + I(D_wind_high * 
    wind_speed) + meanpressure
  Res.Df    RSS Df Sum of Sq     F Pr(>F)
1    110 1170.1                          
2    108 1144.9  2    25.126 1.185 0.3097
resettest(m4, power=2, type="fitted")

    RESET test

data:  m4
RESET = 9.3041, df1 = 1, df2 = 107, p-value = 0.002883

Do modelu boli pridané premenné D_wind_high (indikátor silného vetra) a interakčný člen D_wind_high * wind_speed, aby sa zachytil potenciálny odlišný efekt vetra pri silnom vetre. Reziduály sú podobné ako v predchádzajúcich modeloch, s priemernou odchýlkou okolo 3,26 °C.

Z koeficientov vyplýva, že jedinou významnou premennou zostáva vlhkosť (humidity, p < 0,001), ktorá má negatívny vplyv na teplotu. Ostatné premenné vrátane rýchlosti vetra, indikátora silného vetra, interakčného člena a priemerného tlaku nie sú štatisticky významné.

Model vysvetľuje približne 75 % variability teploty (R² = 0,749), čo je len mierne zlepšenie oproti pôvodnému modelu. Porovnanie s jednoduchším modelom cez analýzu variance ukazuje, že pridanie indikátora a interakcie neprinieslo štatisticky významné zlepšenie (p = 0,31). RESET test však ukazuje p-hodnotu 0,0029, čo naznačuje, že model môže byť stále nesprávne špecifikovaný a nelineárne vzťahy medzi premennými nie sú úplne zachytené.

Celkovo pridanie indikátora silného vetra a interakcie nezlepšilo významne predikciu, vlhkosť zostáva hlavnou premennou ovplyvňujúcou teplotu a model stále nemusí úplne zachytávať všetky nelineárne vzťahy.

Zhrnutie výsledkov

Model Adj R² RESET p-hodnota Poznámka
Základný lineárny 0.737 0.00741 Test signifikantný
Kvadratický 0.842 0.00422 Lepší fit, ale RESET stále signifikantný
Piecewise 0.738 0.00288 Mierne zlepšenie
Box–Cox 0.733 0.0602 Transformácia znižuje odchýlku

Záver

  • Ramsey RESET test je signifikantný pri väčšine modelov → modely pravdepodobne nie sú úplne správne špecifikované.
  • Kvadratické členy zlepšujú fit (Adj R² z ~0.74 na ~0.84), no nezbavujú sa problému špecifikácie.
  • Box–Cox transformácia navrhuje lambda ≈ -0.12, čo naznačuje miernu nelineárnu úpravu cieľovej premennej.
  • Odporúčané rozšírenia: pridať sezónne termy (mesiac, deň roka), interakcie a skúmať autokoreláciu rezíduí.
LS0tCnRpdGxlOiAiTmVsaW5lw6FybmUgxaFwZWNpZmlrw6FjaWUiCmF1dGhvcjogIk3DoXJpYSBNYXTDusWhb3bDoSIKZGF0ZTogIjIwMjUtMTEtMTMiCm91dHB1dDoKICBodG1sX25vdGVib29rOgogICAgdG9jOiB0cnVlCiAgICB0b2NfZmxvYXQ6IHRydWUKICAgIHRoZW1lOiBjb3NtbwogICAgaGlnaGxpZ2h0OiB6ZW5idXJuCiAgaHRtbF9kb2N1bWVudDoKICAgIHRvYzogdHJ1ZQogICAgZGZfcHJpbnQ6IHBhZ2VkCmVkaXRvcl9vcHRpb25zOgogIG1hcmtkb3duOgogICAgd3JhcDogNzIKLS0tCgpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KGVjaG8gPSBUUlVFLCBtZXNzYWdlID0gRkFMU0UsIHdhcm5pbmcgPSBGQUxTRSkKbGlicmFyeSh0aWR5dmVyc2UpCmxpYnJhcnkoY2FyKQpsaWJyYXJ5KGxtdGVzdCkKbGlicmFyeShzYW5kd2ljaCkKbGlicmFyeShnZ3Bsb3QyKQpsaWJyYXJ5KGJyb29tKQpsaWJyYXJ5KE1BU1MpCmBgYAoKIyDDmnZvZAoKVGVudG8gZG9rdW1lbnQgYW5hbHl6dWplIGRhdGFzZXQgKipEYWlseURlbGhpQ2xpbWF0ZVRlc3QuY3N2KiosIGt0b3LDvSBvYnNhaHVqZSBkZW5uw7oga2zDrW11IHYgRGlsbMOtLiAKRMOhdGEgb2JzYWh1asO6IDExNCBwb3pvcm92YW7DrSBhIHRpZXRvIHByZW1lbm7DqToKCi0gYGRhdGVgIOKAlCBkw6F0dW0gbWVyYW5pYSAgCi0gYG1lYW50ZW1wYCDigJQgcHJpZW1lcm7DoSBkZW5uw6EgdGVwbG90YSAowrBDKSAgCi0gYGh1bWlkaXR5YCDigJQgcHJpZW1lcm7DoSByZWxhdMOtdm5hIHZsaGtvc8WlICglKSAgCi0gYHdpbmRfc3BlZWRgIOKAlCBwcmllbWVybsOhIHLDvWNobG9zxaUgdmV0cmEgIAotIGBtZWFucHJlc3N1cmVgIOKAlCBwcmllbWVybsO9IGF0bW9zZsOpcmlja8O9IHRsYWsgIAoKQ2llxL5vbSBhbmFsw716eSBqZSBvdmVyacWlIMWhcGVjaWZpa8OhY2l1IG1vZGVsdSBwb21vY291ICoqUmFtc2V5IFJFU0VUIHRlc3R1KiosIHByZXNrw7ptYcWlIG1vxb5uw6kgbmVsaW5lYXJpdHkgYSB0cmFuc2Zvcm3DoWNpZSwgCmEgcG9yb3ZuYcWlIGFsdGVybmF0w612bmUgbW9kZWx5IChsaW5lw6FybnksIGt2YWRyYXRpY2vDvSwgYSBtb2RlbCBzIGxvbW9tIHBvZMS+YSByw71jaGxvc3RpIHZldHJhKS4KCiMgTmHEjcOtdGFuaWUgZMOhdAoKYGBge3J9CmRmIDwtIHJlYWQuY3N2KCJEYWlseURlbGhpQ2xpbWF0ZVRlc3QuY3N2IikKc3VtbWFyeShkZikKZ2xpbXBzZShkZikKYGBgCgojIFrDoWtsYWRuw70gbGluZcOhcm55IG1vZGVsCgpgYGB7cn0KbTEgPC0gbG0obWVhbnRlbXAgfiBodW1pZGl0eSArIHdpbmRfc3BlZWQgKyBtZWFucHJlc3N1cmUsIGRhdGEgPSBkZikKc3VtbWFyeShtMSkKYGBgClbDvXNsZWRreSBsaW5lw6FybmVqIHJlZ3Jlc2llIHNrw7ptYWrDumNlIHZwbHl2IHZsaGtvc3RpLCByw71jaGxvc3RpIHZldHJhIGEgcHJpZW1lcm7DqWhvIHRsYWt1IG5hIHByaWVtZXJuw7ogdGVwbG90dSB1a2F6dWrDuiwgxb5lIG1vZGVsIGRva8Ohxb5lIHZ5c3ZldGxpxaUgcHJpYmxpxb5uZSAqNzQsNCAlIHZhcmlhYmlsaXR5IHRlcGxvdHkqLCDEjW8gamUgdmXEvm1pIGRvYnLDqS4gKlByaWVtZXJuw6Egb2RjaMO9bGthIHByZWRwb3ZlZMOtIG9kIHNrdXRvxI1uw71jaCBob2Ruw7R0IGplIHByaWJsacW+bmUgMywyNiDCsEMgYSBtZWRpw6FuIHJlemlkdcOhbG92IGplIHRha21lciBudWxvdsO9KiwgxI1vIG5hem5hxI11amUsIMW+ZSBtb2RlbCBqZSB2byB2w6TEjcWhaW5lIHByw61wYWRvdiBwcmVzbsO9LiBKZWRpbm91IHByZW1lbm5vdSBzbyDFoXRhdGlzdGlja3kgdsO9em5hbW7DvW0gdnBseXZvbSBqZSB2bGhrb3PFpSwga3RvcsOhIG3DoSBuZWdhdMOtdm55IGVmZWt0IOKAkyBzIGthxb5kw71tIHp2w73FoWVuw61tIHZsaGtvc3RpIG8gamVkbnUgamVkbm90a3Uga2xlc8OhIHRlcGxvdGEgcHJpYmxpxb5uZSBvIDAsMyDCsEMuIFLDvWNobG9zxaUgdmV0cmEgYSBwcmllbWVybsO9IHRsYWsgc2EgdWvDoXphbGkgYWtvIG5ldsO9em5hbW7DqSwgaWNoIHZwbHl2IG5hIHRlcGxvdHUgbmllIGplIMWhdGF0aXN0aWNreSBwcmV1a8OhemFuw70uIEludGVyY2VwdCBtb2RlbHUgdWTDoXZhIHByZWRwb2tsYWRhbsO6IHByaWVtZXJuw7ogdGVwbG90dSwga2XEjyBzw7ogdsWhZXRreSB0cmkgcHJlbWVubsOpIG51bG92w6ksIGEgamUgcHJpYmxpxb5uZSA0Miw4IMKwQy4gQ2Vsa292byBqZSBtb2RlbCDFoXRhdGlzdGlja3kgdsO9em5hbW7DvSwgxI1vIHBvdHZyZHp1amUgdnlzb2vDoSBob2Rub3RhIEYtxaF0YXRpc3Rpa3kuICpSZXppZHXDoWx5IG5hem5hxI11asO6LCDFvmUgbmlla3RvcsOpIHByZWRwb3ZlZGUgc2Egb2RjaHnEvnVqw7ogYcW+IG8gNuKAkzcgc3R1cMWIb3Ygb2Qgc2t1dG/EjW7DvWNoIGhvZG7DtHQsIG5vIHbDpMSNxaFpbmEgb2RjaMO9bG9rIGplIG1lbsWhaWEuKiBaIHbDvXNsZWRrb3YgdnlwbMO9dmEsIMW+ZSB2bGhrb3PFpSBqZSBrxL7DusSNb3bDvW0gZmFrdG9yb20gb3ZwbHl2xYh1asO6Y2ltIHByaWVtZXJuw7ogdGVwbG90dSB2IGFuYWx5em92YW7DvWNoIGTDoXRhY2gsIHphdGlhxL4gxI1vIG9zdGF0bsOpIHByZW1lbm7DqSBtYWrDuiB6YW5lZGJhdGXEvm7DvSBlZmVrdC4gTW9kZWwgdGVkYSBwb3NreXR1amUgc3BvxL5haGxpdsO9IG7DoWjEvmFkIG5hIHrDoXZpc2xvc8WlIHRlcGxvdHkgb2QgbWV0ZW9yb2xvZ2lja8O9Y2ggZmFrdG9yb3YsIHByacSNb20gamVobyBwcmVkcG92ZWRlIHPDuiB2byB2xaFlb2JlY25vc3RpIHByZXNuw6kgYSB2eXN2ZXTEvnVqw7ogdsOkxI3FoWludSB2YXJpYWJpbGl0eSBkw6F0LgoKIyMgUmFtc2V5IFJFU0VUIHRlc3QKCmBgYHtyfQpyZXNldHRlc3QobTEsIHBvd2VyID0gMiwgdHlwZSA9ICJmaXR0ZWQiKQpgYGAKVsO9c2xlZGt5IFJFU0VUIHRlc3R1IChSZWdyZXNzaW9uIEVxdWF0aW9uIFNwZWNpZmljYXRpb24gRXJyb3IgVGVzdCkgcHJlIG1vZGVsIG0xIHVrYXp1asO6LCDFvmUgaG9kbm90YSB0ZXN0b3ZlaiDFoXRhdGlzdGlreSBqZSA3LDQ0NzEgcyAxIGEgMTA5IHN0dXDFiGFtaSB2b8S+bm9zdGkgYSBwLWhvZG5vdGEgamUgMCwwMDc0LiBUbyBuYXpuYcSNdWplLCDFvmUgbW9kZWwgbcO0xb5lIGJ5xaUgbmVzcHLDoXZuZSDFoXBlY2lmaWtvdmFuw70sIHRlZGEgbGluZcOhcm5hIGZvcm1hIG1vZGVsdSBuZW11c8OtIMO6cGxuZSB6YWNoeXTDoXZhxaUgdnrFpWFoIG1lZHppIHByZW1lbm7DvW1pLiBQLWhvZG5vdGEgbmnFvsWhaWEgYWtvIDAsMDUgem5hbWVuw6EsIMW+ZSBleGlzdHVqZSDFoXRhdGlzdGlja3kgdsO9em5hbW7DvSBkw7RrYXogcHJvdGkgbnVsb3ZlaiBoeXBvdMOpemUsIHBvZMS+YSBrdG9yZWogamUgbW9kZWwgc3Byw6F2bmUgxaFwZWNpZmlrb3ZhbsO9LiBJbsO9bWkgc2xvdmFtaSwgbW9kZWwgcHJhdmRlcG9kb2JuZSB2eW5lY2jDoXZhIG5lamFrw6kgbmVsaW5lw6FybmUgdnrFpWFoeSBhbGVibyBpbnRlcmFrY2llIG1lZHppIHByZW1lbm7DvW1pLCBrdG9yw6kgYnkgbW9obGkgemxlcMWhacWlIGplaG8gcHJlc25vc8WlLgoKCiMjIERpYWdub3N0aWNrw6kgZ3JhZnkKCmBgYHtyIGZpZy53aWR0aD02LCBmaWcuaGVpZ2h0PTR9CnBhcihtZnJvdyA9IGMoMSwxKSkKcGxvdChtMSRmaXR0ZWQudmFsdWVzLCByZXNpZChtMSksIG1haW49IlJlc2lkdWFscyB2cyBGaXR0ZWQgKGJhc2VsaW5lKSIsIHhsYWI9IkZpdHRlZCB2YWx1ZXMiLCB5bGFiPSJSZXNpZHVhbHMiKQphYmxpbmUoaD0wLCBjb2w9InJlZCIsIGx0eT0yKQpgYGAKRGlhZ25vc3RpY2vDvSBncmFmICJadnnFoWt5IHZzLiBTcG/EjcOtdGFuw6kgaG9kbm90eSIgc2zDusW+aSBuYSBvdmVyZW5pZSBrxL7DusSNb3bDvWNoIHByZWRwb2tsYWRvdiBsaW5lw6FybmVqIHJlZ3Jlc2llLiBOYSBncmFmZSB2aWTDrW1lIG7DoWhvZG7DvSBhIHJvdm5vbWVybsO9IHJvenB0eWwgYm9kb3Ygb2tvbG8gxI1lcnZlbmVqIG51bG92ZWogbMOtbmllICgkeT0wJCkuIFRvIGplIGlkZcOhbG55IHN0YXYuVGVudG8gcm96cHR5bCBiZXogdmlkaXRlxL5uw6lobyBzeXN0w6ltb3bDqWhvIHZ6b3J1IChha28gbGlldmlrIGFsZWJvIGtyaXZrYSkgc2lsbmUgbmF6bmHEjXVqZSwgxb5lOkJvbCBzcGxuZW7DvSBwcmVkcG9rbGFkIGhvbW9za2VkYXN0aWNpdHkgKGtvbsWhdGFudG7DvSByb3pwdHlsIGNow71iKS5WesWlYWggbWVkemkgcHJlbWVubsO9bWkgamUgcHJhdmRlcG9kb2JuZSBsaW5lw6FybnkuTW9kZWwgJG0xJCBqZSB0ZWRhIHByaW1lcmFuZSB2aG9kbsO9IGEgc3DEusWIYSB6w6FrbGFkbsOpIHByZWRwb2tsYWR5IG8gcm96cHR5bGUuIFYgZ3JhZmUgc2EgbmFjaMOhZHphIGxlbiBuaWVrb8S+a28gZXh0csOpbW55Y2ggaG9kbsO0dCAob3V0bGllcnMpLCBrdG9yw6kgYnkgYm9sbyB2aG9kbsOpIGRvZGF0b8SNbmUgcHJlc2vDum1hxaUuCgoKYGBge3IgZmlnLndpZHRoPTcsIGZpZy5oZWlnaHQ9NX0KY2FyOjpjclBsb3RzKG0xKQpgYGAKVGlldG8gZ3JhZnkgcG9yb3Zuw6F2YWrDuiBsaW5lw6FybnkgdnrFpWFoIG1vZGVsdSAobW9kcsOhIMSNaWFyYSkgc28gc2t1dG/EjW7DvW0gdHZhcm9tIHZ6xaVhaHUgdiBkw6F0YWNoIChydcW+b3bDoSDEjWlhcmEpLkh1bWlkaXR5IChWbGhrb3PFpSk6IE1vZHLDoSBhIHJ1xb5vdsOhIMSNaWFyYSBzw7ogc2kgdmXEvm1pIGJsw616a2UuIExpbmXDoXJueSB2esWlYWggYm9sIHNwcsOhdm5lIMWhcGVjaWZpa292YW7DvS5XaW5kX3NwZWVkIChSw71jaGxvc8WlIHZldHJhKTogxIxpYXJ5IHPDuiB0YWt0aWXFviBibMOtemtlLiBMaW5lw6FybnkgcHJlZHBva2xhZCBqZSB2IHBvcmlhZGt1LiBNZWFucHJlc3N1cmUgKFByaWVtZXJuw70gdGxhayk6ICpSdcW+b3bDoSDEjWlhcmEgc2EgZHJhbWF0aWNreSBvZGNoecS+dWplIG9kIG1vZHJlai4gVG8gamUgamFzbsO9IGTDtGtheiwgxb5lIHByZWRwb2tsYWQgbGluZWFyaXR5IGJvbCBwb3J1xaFlbsO9LioKCgojIEt2YWRyYXRpY2vDvSBtb2RlbAoKYGBge3J9Cm0yIDwtIGxtKG1lYW50ZW1wIH4gaHVtaWRpdHkgKyB3aW5kX3NwZWVkICsgbWVhbnByZXNzdXJlICsKICAgICAgICAgICAgSShodW1pZGl0eV4yKSArIEkod2luZF9zcGVlZF4yKSArIEkobWVhbnByZXNzdXJlXjIpLCBkYXRhID0gZGYpCnN1bW1hcnkobTIpCmFub3ZhKG0xLCBtMikKcmVzZXR0ZXN0KG0yLCBwb3dlcj0yLCB0eXBlPSJmaXR0ZWQiKQpgYGAKRG8gbW9kZWx1IGJvbGkgcHJpZGFuw6kga3ZhZHJhdGlja8OpIMSNbGVueSBwcmVtZW5uw71jaCwgYWJ5IHNhIHphY2h5dGlsaSBuZWxpbmXDoXJuZSB2esWlYWh5IHMgcHJpZW1lcm5vdSB0ZXBsb3RvdS4gKlJlemlkdcOhbHkgc2Egem1lbsWhaWxpLCBwcmllbWVybmUgc2Egb2RjaHnEvnVqw7ogbyAyLDUzIMKwQyosIMSNbyBuYXpuYcSNdWplIGxlcMWhaWUgcHJpc3DDtHNvYmVuaWUgZMOhdGFtLiBWw716bmFtbsO9IHZwbHl2IG5hIHRlcGxvdHUgbWFqw7ogdmxoa29zxaUsIHByaWVtZXJuw70gdGxhayBhIGplaG8ga3ZhZHJhdGlja8O9IMSNbGVuLCBvc3RhdG7DqSDEjWxlbnkgbmllIHPDuiDFoXRhdGlzdGlja3kgdsO9em5hbW7DqS4gTW9kZWwgdnlzdmV0xL51amUgcHJpYmxpxb5uZSA4NCAlIHZhcmlhYmlsaXR5IHRlcGxvdHkgYSBwcmlkYW5pZSBrdmFkcmF0aWNrw71jaCDEjWxlbm92IHbDvXpuYW1uZSB6bGVwxaFpbG8gamVobyBwcmVzbm9zxaUuICpSRVNFVCB0ZXN0IHbFoWFrIHN0w6FsZSBuYXpuYcSNdWplLCDFvmUgbW9kZWwgbmVtdXPDrSDDunBsbmUgemFjaHl0w6F2YcWlIHbFoWV0a3kgbmVsaW5lw6FybmUgdnrFpWFoeSAocCA9IDAsMDA0MikuKiBDZWxrb3ZvIG5vdsO9IG1vZGVsIGxlcMWhaWUgdnlzdGlodWplIHrDoXZpc2xvc8WlIHRlcGxvdHkgb2QgbWV0ZW9yb2xvZ2lja8O9Y2ggZmFrdG9yb3YsIG5vIHN0w6FsZSBleGlzdHVqZSBwcmllc3RvciBuYSB6bGVwxaFlbmllLgoKCiMgQm944oCTQ294IHRyYW5zZm9ybcOhY2lhIGNpZcS+b3ZlaiBwcmVtZW5uZWoKCmBgYHtyfQpiYyA8LSBib3hjb3gobTEsIGRhdGEgPSBkZikKKGxhbWJkYSA8LSBiYyR4W3doaWNoLm1heChiYyR5KV0pCmBgYApHcmFmIEJveC1Db3ggdHJhbnNmb3Jtw6FjaWUgaMS+YWTDoSBvcHRpbcOhbG51IGhvZG5vdHUgIHByZSB0cmFuc2Zvcm3DoWNpdSB6w6F2aXNsZWogcHJlbWVubmVqLiAqT3B0aW3DoWxuYSBob2Rub3RhOiogVnJjaG9sIGtyaXZreSAobWF4aW11bSBsb2ctdmllcm9ob2Rub3N0aSkgamUgcHJpYmxpxb5uZSBwcmkgbGFtYmRlIDAsMzQuICpJbnRlcnZhbCBzcG/EvmFobGl2b3N0aToqIDk1JSBpbnRlcnZhbCBzcG/EvmFobGl2b3N0aSBwcmUgbGFtZHUgKG9ocmFuacSNZW7DvSB6dmlzbMO9bWkgxI1pYXJhbWkpIHphaMWVxYhhIGhvZG5vdHUgLgoKCmBgYHtyfQpkZiRtZWFudGVtcF9iYyA8LSAoZGYkbWVhbnRlbXBebGFtYmRhIC0gMSkvbGFtYmRhCm0zIDwtIGxtKG1lYW50ZW1wX2JjIH4gaHVtaWRpdHkgKyB3aW5kX3NwZWVkICsgbWVhbnByZXNzdXJlLCBkYXRhID0gZGYpCnN1bW1hcnkobTMpCnJlc2V0dGVzdChtMywgcG93ZXI9MiwgdHlwZT0iZml0dGVkIikKYGBgCk1vZGVsIHMgdHJhbnNmb3Jtb3Zhbm91IHRlcGxvdG91IChtZWFudGVtcF9iYykgdmXEvm1pIGRvYnJlIHByZWRwb3ZlZMOhIGhvZG5vdHksIHJlemlkdcOhbHkgc8O6IG1hbMOpICjCsTAsNDMpLiBWw716bmFtbsO9IHZwbHl2IG5hIHRlcGxvdHUgbcOhIGliYSB2bGhrb3PFpSwga3RvcsOhIG3DoSBuZWdhdMOtdm55IGVmZWt0LCB6YXRpYcS+IMSNbyByw71jaGxvc8WlIHZldHJhIGEgcHJpZW1lcm7DvSB0bGFrIG5pZSBzw7ogdsO9em5hbW7DqS4gTW9kZWwgdnlzdmV0xL51amUgcHJpYmxpxb5uZSA3NCAlIHZhcmlhYmlsaXR5IHRlcGxvdHkgYSBqZSDFoXRhdGlzdGlja3kgdsO9em5hbW7DvS4gUkVTRVQgdGVzdCAocCA9IDAsMDYwKSBuZW5hem5hxI11amUgdsO9cmF6bsO6IG5lc3Byw6F2bnUgxaFwZWNpZmlrw6FjaXUgbW9kZWx1LCB0YWvFvmUgbGluZcOhcm5hIGZvcm1hIHMgdHJhbnNmb3Jtb3Zhbm91IHByZW1lbm5vdSBqZSB2aG9kbsOhLgoKCiMgUGllY2V3aXNlIChsb21vdsO9KSBtb2RlbCBwb2TEvmEgd2luZF9zcGVlZCA+IDEwCgpgYGB7cn0KZGYgPC0gZGYgJT4lIG11dGF0ZShEX3dpbmRfaGlnaCA9IGlmZWxzZSh3aW5kX3NwZWVkID4gMTAsIDEsIDApKQptNCA8LSBsbShtZWFudGVtcCB+IGh1bWlkaXR5ICsgd2luZF9zcGVlZCArIERfd2luZF9oaWdoICsgSShEX3dpbmRfaGlnaCp3aW5kX3NwZWVkKSArIG1lYW5wcmVzc3VyZSwgZGF0YT1kZikKc3VtbWFyeShtNCkKYW5vdmEobTEsIG00KQpyZXNldHRlc3QobTQsIHBvd2VyPTIsIHR5cGU9ImZpdHRlZCIpCmBgYApEbyBtb2RlbHUgYm9saSBwcmlkYW7DqSBwcmVtZW5uw6kgRF93aW5kX2hpZ2ggKGluZGlrw6F0b3Igc2lsbsOpaG8gdmV0cmEpIGEgaW50ZXJha8SNbsO9IMSNbGVuIERfd2luZF9oaWdoICogd2luZF9zcGVlZCwgYWJ5IHNhIHphY2h5dGlsIHBvdGVuY2nDoWxueSBvZGxpxaFuw70gZWZla3QgdmV0cmEgcHJpIHNpbG5vbSB2ZXRyZS4gUmV6aWR1w6FseSBzw7ogcG9kb2Juw6kgYWtvIHYgcHJlZGNow6FkemFqw7pjaWNoIG1vZGVsb2NoLCBzIHByaWVtZXJub3Ugb2RjaMO9bGtvdSBva29sbyAzLDI2IMKwQy4KCloga29lZmljaWVudG92IHZ5cGzDvXZhLCDFvmUgamVkaW5vdSB2w716bmFtbm91IHByZW1lbm5vdSB6b3N0w6F2YSB2bGhrb3PFpSAoaHVtaWRpdHksIHAgPCAwLDAwMSksIGt0b3LDoSBtw6EgbmVnYXTDrXZueSB2cGx5diBuYSB0ZXBsb3R1LiBPc3RhdG7DqSBwcmVtZW5uw6kgdnLDoXRhbmUgcsO9Y2hsb3N0aSB2ZXRyYSwgaW5kaWvDoXRvcmEgc2lsbsOpaG8gdmV0cmEsIGludGVyYWvEjW7DqWhvIMSNbGVuYSBhIHByaWVtZXJuw6lobyB0bGFrdSBuaWUgc8O6IMWhdGF0aXN0aWNreSB2w716bmFtbsOpLgoKTW9kZWwgdnlzdmV0xL51amUgcHJpYmxpxb5uZSA3NSAlIHZhcmlhYmlsaXR5IHRlcGxvdHkgKFLCsiA9IDAsNzQ5KSwgxI1vIGplIGxlbiBtaWVybmUgemxlcMWhZW5pZSBvcHJvdGkgcMO0dm9kbsOpbXUgbW9kZWx1LiBQb3Jvdm5hbmllIHMgamVkbm9kdWNoxaHDrW0gbW9kZWxvbSBjZXogYW5hbMO9enUgdmFyaWFuY2UgdWthenVqZSwgxb5lIHByaWRhbmllIGluZGlrw6F0b3JhIGEgaW50ZXJha2NpZSBuZXByaW5pZXNsbyDFoXRhdGlzdGlja3kgdsO9em5hbW7DqSB6bGVwxaFlbmllIChwID0gMCwzMSkuIFJFU0VUIHRlc3QgdsWhYWsgdWthenVqZSBwLWhvZG5vdHUgMCwwMDI5LCDEjW8gbmF6bmHEjXVqZSwgxb5lIG1vZGVsIG3DtMW+ZSBiecWlIHN0w6FsZSBuZXNwcsOhdm5lIMWhcGVjaWZpa292YW7DvSBhIG5lbGluZcOhcm5lIHZ6xaVhaHkgbWVkemkgcHJlbWVubsO9bWkgbmllIHPDuiDDunBsbmUgemFjaHl0ZW7DqS4KCkNlbGtvdm8gcHJpZGFuaWUgaW5kaWvDoXRvcmEgc2lsbsOpaG8gdmV0cmEgYSBpbnRlcmFrY2llIG5lemxlcMWhaWxvIHbDvXpuYW1uZSBwcmVkaWtjaXUsIHZsaGtvc8WlIHpvc3TDoXZhIGhsYXZub3UgcHJlbWVubm91IG92cGx5dsWIdWrDumNvdSB0ZXBsb3R1IGEgbW9kZWwgc3TDoWxlIG5lbXVzw60gw7pwbG5lIHphY2h5dMOhdmHFpSB2xaFldGt5IG5lbGluZcOhcm5lIHZ6xaVhaHkuCgoKIyBaaHJudXRpZSB2w71zbGVka292Cgp8IE1vZGVsIHwgQWRqIFLCsiB8IFJFU0VUIHAtaG9kbm90YSB8IFBvem7DoW1rYSB8Cnw6LS0tLS0tfDotLS0tLS0tLXw6LS0tLS0tLS0tLS0tLS0tLXw6LS0tLS0tLS0tLXwKfCBaw6FrbGFkbsO9IGxpbmXDoXJueSB8IGByIHJvdW5kKHN1bW1hcnkobTEpJGFkai5yLnNxdWFyZWQsIDMpYCB8IGByIHNpZ25pZihyZXNldHRlc3QobTEsIHBvd2VyPTIpJHAudmFsdWUsMylgIHwgVGVzdCBzaWduaWZpa2FudG7DvSB8CnwgS3ZhZHJhdGlja8O9IHwgYHIgcm91bmQoc3VtbWFyeShtMikkYWRqLnIuc3F1YXJlZCwgMylgIHwgYHIgc2lnbmlmKHJlc2V0dGVzdChtMiwgcG93ZXI9MikkcC52YWx1ZSwzKWAgfCBMZXDFocOtIGZpdCwgYWxlIFJFU0VUIHN0w6FsZSBzaWduaWZpa2FudG7DvSB8CnwgUGllY2V3aXNlIHwgYHIgcm91bmQoc3VtbWFyeShtNCkkYWRqLnIuc3F1YXJlZCwgMylgIHwgYHIgc2lnbmlmKHJlc2V0dGVzdChtNCwgcG93ZXI9MikkcC52YWx1ZSwzKWAgfCBNaWVybmUgemxlcMWhZW5pZSB8CnwgQm944oCTQ294IHwgYHIgcm91bmQoc3VtbWFyeShtMykkYWRqLnIuc3F1YXJlZCwgMylgIHwgYHIgc2lnbmlmKHJlc2V0dGVzdChtMywgcG93ZXI9MikkcC52YWx1ZSwzKWAgfCBUcmFuc2Zvcm3DoWNpYSB6bmnFvnVqZSBvZGNow71sa3UgfAoKIyBaw6F2ZXIKCi0gUmFtc2V5IFJFU0VUIHRlc3QgamUgc2lnbmlmaWthbnRuw70gcHJpIHbDpMSNxaFpbmUgbW9kZWxvdiDihpIgbW9kZWx5IHByYXZkZXBvZG9ibmUgbmllIHPDuiDDunBsbmUgc3Byw6F2bmUgxaFwZWNpZmlrb3ZhbsOpLiAgCi0gS3ZhZHJhdGlja8OpIMSNbGVueSB6bGVwxaF1asO6IGZpdCAoQWRqIFLCsiB6IH4wLjc0IG5hIH4wLjg0KSwgbm8gbmV6YmF2dWrDuiBzYSBwcm9ibMOpbXUgxaFwZWNpZmlrw6FjaWUuICAKLSBCb3jigJNDb3ggdHJhbnNmb3Jtw6FjaWEgbmF2cmh1amUgbGFtYmRhIOKJiCAtMC4xMiwgxI1vIG5hem5hxI11amUgbWllcm51IG5lbGluZcOhcm51IMO6cHJhdnUgY2llxL5vdmVqIHByZW1lbm5lai4gIAotIE9kcG9yw7rEjWFuw6kgcm96xaHDrXJlbmlhOiBwcmlkYcWlIHNlesOzbm5lIHRlcm15IChtZXNpYWMsIGRlxYggcm9rYSksIGludGVyYWtjaWUgYSBza8O6bWHFpSBhdXRva29yZWzDoWNpdSByZXrDrWR1w60uCgo=