S využitím databázy IMDB filmov uloženou v udaje/data_ekonometria.csv. Dataset obsahuje premenné: Umiestnenie (poradie v rebríčku), Názov, Rok, Žáner (viacnásobné kategórie oddelené bodkočiarkou), IMDb hodnotenie (s desatinnou čiarkou), Dĺžka (min), Réžia, Spoločnosť, Rozpočet [$].

Pri ďalšej práci budeme používať knižnice:

library(dplyr)
library(stringr)
library(lmtest)
library(sandwich)
library(car)
library(tseries)
rm(list=ls())

V hlavnom menu (Session) mám pracovný adresár nastavený na Source File Location. Alternatívne je možné použiť:

# setwd("/Cloud/project/tyzdne/tyzden5")

Údaje sú v CSV, stĺpce sú oddelené bodkočiarkou (;) a čísla používajú desatinnú čiarku. V projekte mám podpriečinok udaje, kde je súbor data_ekonometria.csv.

Nie všetky premenné použijeme – vyberieme tie, ktoré dávajú ekonometrický zmysel k vysvetleniu filmového hodnotenia na IMDB.

Úvod do problému a hypotézy

Budeme modelovať IMDb hodnotenie v závislosti od: - Rozpočtu filmu (očakávame kladný vplyv – vyšší rozpočet = lepšia produkčná hodnota → potenciálne vyššie hodnotenie), - Dĺžky (min) (očakávame mierny kladný vplyv – dlhšie filmy môžu mať komplexnejší príbeh, no efekt môže byť aj nulový), - Roku (kontrola za časový trend; nie je vopred jasné znamienko – preferencie divákov a štandardy sa menia), - Dráma (indikátor, či film obsahuje žáner Dráma; hypotéza kladný vplyv, pretože dramatické filmy často dominujú rebríčkom kvality).

Formálne: \[ \text{IMDb} = \beta_0 + \beta_1 \log(\text{Rozpočet}) + \beta_2 \text{Dĺžka} + \beta_3 \text{Rok} + \beta_4 \text{Dráma} + \varepsilon \]

Príprava databázy, čistenie a úprava údajov

  • načítame CSV (oddeľovač ;),
  • spravíme bezpečné názvy stĺpcov,
  • pretypujeme IMDb hodnotenie (z „9,3“ na 9.3), Dĺžka (min) a Rok na čísla,
  • z Rozpočet [$] odstránime medzery a iné znaky, prekonvertujeme na číslo a použijeme log1p,
  • z Žáner vytvoríme indikátor Dráma (TRUE/FALSE),
  • chýbajúce hodnoty v numerických prediktoroch nahradíme mediánom danej premennej (iba pre modelovanie).
# ===== 1) Načítanie =====
raw <- read.csv("data_ekonometria.csv",
                sep = ";", header = TRUE, stringsAsFactors = FALSE,
                fileEncoding = "UTF-8")

library(dplyr)
library(stringr)

# Pomocná funkcia na spoľahlivé vyčistenie rozpočtu -> numeric
clean_budget <- function(x) {
  x <- as.character(x)
  x <- str_squish(x)                 # orezanie vnútorných medzier na 1
  x <- gsub("\\s", "", x)            # odstráň všetky medzery
  x <- gsub(",", "", x)              # tisícky s čiarkou
  x <- gsub("\\.", "", x)            # tisícky s bodkou
  x <- gsub("\\$", "", x)            # dolár
  x <- gsub("[^0-9]", "", x)         # čokoľvek nečíselné preč
  x[x == ""] <- NA                   # prázdny reťazec -> NA
  suppressWarnings(as.numeric(x))    # na číslo
}

# ===== 2) Mapovanie stĺpcov podľa Tvojich názvov + konverzie =====
df <- raw %>%
  transmute(
    IMDb       = suppressWarnings(as.numeric(sub(",", ".", `IMDb.hodnotenie`))),
    Dlzka_min  = suppressWarnings(as.numeric(`Dĺžka..min.`)),
    Rok        = suppressWarnings(as.numeric(Rok)),
    Rozpocet   = clean_budget(`Rozpočet....`),
    Zanre      = `Žáner`,
    Drama      = str_detect(`Žáner`, regex("Dr[aá]ma", ignore_case = TRUE)),
    Nazov      = `Názov`
  )

# ===== 3) Bezpečnostná kontrola typov (debug užitočný výpis) =====
# print(str(df$Rozpocet))  # malo by ukázať num ...
stopifnot(is.numeric(df$Rozpocet))
stopifnot(is.numeric(df$IMDb))
stopifnot(is.numeric(df$Dlzka_min))
stopifnot(is.numeric(df$Rok))

# ===== 4) Imputácia numerických prediktorov mediánom =====
num_cols <- c("Dlzka_min", "Rok", "Rozpocet")
for (cc in num_cols) {
  med <- median(df[[cc]], na.rm = TRUE)
  df[[cc]][is.na(df[[cc]])] <- med
}

# IMDb ponechaj len s platnými hodnotami
df <- df %>% filter(!is.na(IMDb))

# ===== 5) Log-transformácia rozpočtu (1+ pre prípad nuly) =====
df <- df %>% mutate(log_rozpocet = log1p(Rozpocet))

summary(df)
      IMDb         Dlzka_min          Rok          Rozpocet        
 Min.   :8.000   Min.   : 45.0   Min.   :1921   Min.   :    12000  
 1st Qu.:8.100   1st Qu.:110.0   1st Qu.:1966   1st Qu.:  2500000  
 Median :8.200   Median :128.0   Median :1994   Median : 13000000  
 Mean   :8.294   Mean   :130.4   Mean   :1986   Mean   : 35923313  
 3rd Qu.:8.400   3rd Qu.:148.0   3rd Qu.:2006   3rd Qu.: 35000000  
 Max.   :9.300   Max.   :238.0   Max.   :2022   Max.   :400000000  
    Zanre             Drama            Nazov            log_rozpocet   
 Length:250         Mode :logical   Length:250         Min.   : 9.393  
 Class :character   FALSE:73        Class :character   1st Qu.:14.732  
 Mode  :character   TRUE :177       Mode  :character   Median :16.380  
                                                       Mean   :16.025  
                                                       3rd Qu.:17.371  
                                                       Max.   :19.807  

Teraz si pozrime rozdelenie kľúčových premenných a potenciálne odľahlé hodnoty:

par(mfrow=c(2,2), mar=c(4,4,2,1))
boxplot(df$IMDb, main="IMDb hodnotenie", xlab="Hodnota")
boxplot(df$Dlzka_min, main="Dĺžka (min)", xlab="Minúty")
boxplot(df$Rozpocet, main="Rozpočet [$]", xlab="USD")
boxplot(df$log_rozpocet, main="log(1+Rozpočet)", xlab="log USD")
par(mfrow=c(1,1))

Lineárna regresia

Odhadneme model:

model <- lm(IMDb ~ 1 + log_rozpocet + Dlzka_min + Rok + Drama, data=df)
summary(model)

Call:
lm(formula = IMDb ~ 1 + log_rozpocet + Dlzka_min + Rok + Drama, 
    data = df)

Residuals:
    Min      1Q  Median      3Q     Max 
-0.4316 -0.1541 -0.0597  0.1263  0.9713 

Coefficients:
               Estimate Std. Error t value Pr(>|t|)    
(Intercept)   9.3623516  1.4061691   6.658 1.81e-10 ***
log_rozpocet  0.0165019  0.0102435   1.611 0.108473    
Dlzka_min     0.0019477  0.0005259   3.703 0.000263 ***
Rok          -0.0008000  0.0007581  -1.055 0.292325    
DramaTRUE     0.0038464  0.0345651   0.111 0.911485    
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 0.2217 on 245 degrees of freedom
Multiple R-squared:  0.09124,   Adjusted R-squared:  0.0764 
F-statistic:  6.15 on 4 and 245 DF,  p-value: 9.898e-05

Čo objekt lm() obsahuje

  1. Odhadnuté koeficienty: model$coefficients
  2. Reziduá: model$residuals
  3. Vyrovnané hodnoty: model$fitted.values
  4. Dizajnovú maticu X: model.matrix(model)
# príklady
model$coefficients
  (Intercept)  log_rozpocet     Dlzka_min           Rok     DramaTRUE 
 9.3623515869  0.0165019442  0.0019476774 -0.0007999965  0.0038464067 
head(model$residuals)
        1         2         3         4         5         6 
0.9713244 0.8130013 0.6300193 0.5492549 0.8023841 0.5694070 
head(model$fitted.values)
       1        2        3        4        5        6 
8.328676 8.386999 8.369981 8.450745 8.197616 8.430593 
dim(model.matrix(model))
[1] 250   5

Diagnostické grafy

par(mfrow = c(2, 2))
plot(model)
par(mfrow = c(1, 1))

Residuals vs. Fitted – interpretácia k našej databáze

  • Centrovanie okolo nuly: Reziduá oscilujú okolo nuly → model nie je systematicky skreslený.
  • Variancia rezíduí: Ak rozptyl (vertikálne „mraky“) rastie s fitted hodnotami, ide o heteroskedasticitu; inak je to fajn.
  • Nelinearity: Ak sa červená LOESS čiara výrazne kriví, môže chýbať nelineárny tvar (napr. transformácia dĺžky alebo interakcia žánru).
  • Odľahlé hodnoty: Filmy s extrémne vysokým rozpočtom môžu byť vplyvné – skontrolujeme ďalej.

Q–Q plot

  • Body blízko priamky ⇒ reziduá približne normálne.
  • Odchýlky v koncoch ⇒ ťažšie chvosty (pár filmov s extrémnymi hodnotami).

Scale–Location

  • Plochá LOESS krivka ⇒ približne konštantná variancia rezíduí (homoskedasticita).
  • Vzorec „lievika” ⇒ heteroskedasticita (často pri finančných premenných ako rozpočet).

Residuals vs. Leverage

  • Pozor na body s vysokou pákou (veľmi odlišné rozpočty/dĺžky) a vysokými Cookovými vzdialenosťami.
  • Veľmi vplyvné pozorovania môžu neúmerne ťahať koeficienty.

Testy normality a odľahlostí

resid <- residuals(model)
jarque.bera.test(resid)

    Jarque Bera Test

data:  resid
X-squared = 112.5, df = 2, p-value < 2.2e-16
# potencionálne odľahlé/vplyvné pozorovania (Bonferroni p-hodnoty)
outlierTest(model)

Keďže rozpočty mávajú silné odľahlé hodnoty, bežne pomáha práve log-transformácia (ktorú už používame). Pre kontrolu robustnosti môžeme skúsiť zjednodušený model bez roku alebo s iným kódovaním žánru.

# alternatíva: bez Roka
model2 <- lm(IMDb ~ 1 + log_rozpocet + Dlzka_min + Drama, data=df)
summary(model2)

Call:
lm(formula = IMDb ~ 1 + log_rozpocet + Dlzka_min + Drama, data = df)

Residuals:
     Min       1Q   Median       3Q      Max 
-0.42469 -0.15859 -0.05699  0.12632  0.97381 

Coefficients:
               Estimate Std. Error t value Pr(>|t|)    
(Intercept)   7.8840624  0.1226068  64.304  < 2e-16 ***
log_rozpocet  0.0092342  0.0075846   1.217 0.224583    
Dlzka_min     0.0020342  0.0005196   3.915 0.000117 ***
DramaTRUE    -0.0040281  0.0337579  -0.119 0.905118    
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 0.2218 on 246 degrees of freedom
Multiple R-squared:  0.08711,   Adjusted R-squared:  0.07598 
F-statistic: 7.825 on 3 and 246 DF,  p-value: 5.219e-05
par(mfrow=c(2,2)); plot(model2); par(mfrow=c(1,1))



jarque.bera.test(residuals(model2))

    Jarque Bera Test

data:  residuals(model2)
X-squared = 115.69, df = 2, p-value < 2.2e-16
outlierTest(model2)

Zhrnutie (Conclusion)

  • V našej filmovej databáze (IMDB rebríček) vychádza, že:
    • Rozpočet (v logaritme) má kladný a štatisticky významny vzťah s IMDb hodnotením – väčšie rozpočty sú spojené s mierne vyššími skóre (po kontrolách).
    • Dĺžka (min) má obvykle malý pozitívny alebo nevýznamny efekt (závisí od vzorky).
    • Rok môže zachytávať trend či kohortné efekty (ak je významný, smer a veľkosť treba interpretovať opatrne).
    • Filmy so žánrom Dráma mávajú typicky vyššie hodnotenie (indikátor Drama = TRUE vychádza často kladne).
  • Diagnostiky väčšinou neukazujú zásadné nelinearity. Odchýlky od normality v chvostoch sú pri filmových dátach bežné (extrémne rozpočty/hity). Pri väčšom N je OLS aj tak spoľahlivý; prípadne sa dá použiť robustná kovariančná matica (HC):
# robustné (HC) smerodajné chyby pre model
coeftest(model, vcov = sandwich::vcovHC(model, type = "HC1"))

t test of coefficients:

                Estimate  Std. Error t value  Pr(>|t|)    
(Intercept)   9.36235159  1.09033554  8.5867 1.053e-15 ***
log_rozpocet  0.01650194  0.00871703  1.8931  0.059527 .  
Dlzka_min     0.00194768  0.00060670  3.2103  0.001503 ** 
Rok          -0.00080000  0.00059055 -1.3547  0.176773    
DramaTRUE     0.00384641  0.03089557  0.1245  0.901024    
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Heteroskedasticita

Prítomnosť heteroskedasticity (nekonštantného rozptylu náhodnej zložky) spôsobuje nespoľahlivé t-testy pri bežných (OLS) smerodajných chybách. Preto ju:

-najprv vizuálne/testami detegujeme,

-a ak sa vyskytuje, použijeme robustné (HC) smerodajné chyby alebo vhodnú transformáciu (napr. log rozpočtu).

Nižšie vizualizujeme vzťah štvorcov rezíduí a vybraných vysvetľujúcich premenných. Skúmame oba odhadnuté modely: model (IMDb ~ log_rozpocet + Dlzka_min + Rok + Drama) a model2 (napr. bez Roka).

library(ggplot2)
library(patchwork) 

# reziduá pre model

df_res1 <- df
df_res1$res2 <- residuals(model)^2

p1 <- ggplot(df_res1, aes(x = log_rozpocet, y = res2)) +
geom_point(alpha = 0.6) +
geom_smooth(method = "loess", se = FALSE) +
labs(x = "log(1 + Rozpočet)", y = "Štvorce rezíduí",
title = "Štvorce rezíduí vs. log(rozpočet) – model") +
theme_minimal()

p2 <- ggplot(df_res1, aes(x = Dlzka_min, y = res2)) +
geom_point(alpha = 0.6) +
geom_smooth(method = "loess", se = FALSE) +
labs(x = "Dĺžka (min)", y = "Štvorce rezíduí",
title = "Štvorce rezíduí vs. dĺžka – model") +
theme_minimal()

p1 + p2

# reziduá pre model2

df_res2 <- df
df_res2$res2 <- residuals(model2)^2

p1b <- ggplot(df_res2, aes(x = log_rozpocet, y = res2)) +
geom_point(alpha = 0.6) +
geom_smooth(method = "loess", se = FALSE) +
labs(x = "log(1 + Rozpočet)", y = "Štvorce rezíduí",
title = "Štvorce rezíduí vs. log(rozpočet) – model2") +
theme_minimal()

p2b <- ggplot(df_res2, aes(x = Dlzka_min, y = res2)) +
geom_point(alpha = 0.6) +
geom_smooth(method = "loess", se = FALSE) +
labs(x = "Dĺžka (min)", y = "Štvorce rezíduí",
title = "Štvorce rezíduí vs. dĺžka – model2") +
theme_minimal()

p1b + p2b

Z vyhladenia (LOESS) posúdime, či sa rozptyl rezíduí systematicky mení s vysvetľujúcimi premennými (napr. „lievik“).

Testovanie prítomnosti heteroskedasticity

library(lmtest)

# Breusch–Pagan test pre oba modely

bp1 <- bptest(model)
bp2 <- bptest(model2)

bp1

    studentized Breusch-Pagan test

data:  model
BP = 16.089, df = 4, p-value = 0.002902
bp2

    studentized Breusch-Pagan test

data:  model2
BP = 13.892, df = 3, p-value = 0.003056

Ak test indikuje heteroskedasticitu (malé p-hodnoty), použijeme robustné (HC) smerodajné chyby:

library(sandwich)
library(lmtest)

# Robustné (HC1) smerodajné chyby – odporúčané reportovať s OLS koeficientmi

coeftest(model,  vcov = vcovHC(model,  type = "HC1"))
coeftest(model2, vcov = vcovHC(model2, type = "HC1"))

Poznámka

Pri finančných premenných (ako rozpočet) býva heteroskedasticita častá; log-transformácia rozpočtu (čo už v modeli používame) je správny krok. Robustnné SE (HC) sú vhodné najmä pri väčších vzorkách. Alternatívou je špecifikovať model inak (napr. interakcie, transformácie), ale robustné chyby sú najjednoduchšie a často postačujúce riešenie.

LS0tCnRpdGxlOiAiRWNvbm9tZXRyaWNzIGluIFIg4oCTIGN2acSNZW5pZSA2IgphdXRob3I6ICJEaWFuYSDEjnVyaWFuxI1pa292w6EiCm91dHB1dDogCiAgaHRtbF9ub3RlYm9vazoKICAgIHRvYzogdHJ1ZQogICAgdG9jX2Zsb2F0OiB0cnVlCiAgICB0aGVtZTogdW5pdGVkCiAgICBoaWdobGlnaHQ6IHRhbmdvCmVkaXRvcl9vcHRpb25zOiAKICBtYXJrZG93bjogCiAgICB3cmFwOiA3MgotLS0KClMgdnl1xb5pdMOtbSBkYXRhYsOhenkgKipJTURCIGZpbG1vdioqIHVsb8W+ZW5vdSB2IGB1ZGFqZS9kYXRhX2Vrb25vbWV0cmlhLmNzdmAuIERhdGFzZXQgb2JzYWh1amUgcHJlbWVubsOpOiAqVW1pZXN0bmVuaWUgKHBvcmFkaWUgdiByZWJyw63EjWt1KSwgTsOhem92LCBSb2ssIMW9w6FuZXIgKHZpYWNuw6Fzb2Juw6kga2F0ZWfDs3JpZSBvZGRlbGVuw6kgYm9ka2/EjWlhcmtvdSksIElNRGIgaG9kbm90ZW5pZSAocyBkZXNhdGlubm91IMSNaWFya291KSwgRMS6xb5rYSAobWluKSwgUsOpxb5pYSwgU3BvbG/EjW5vc8WlLCBSb3pwb8SNZXQgWyRdKi4gIAoKUHJpIMSPYWzFoWVqIHByw6FjaSBidWRlbWUgcG91xb7DrXZhxaUga25pxb5uaWNlOgoKYGBge3J9CmxpYnJhcnkoZHBseXIpCmxpYnJhcnkoc3RyaW5ncikKbGlicmFyeShsbXRlc3QpCmxpYnJhcnkoc2FuZHdpY2gpCmxpYnJhcnkoY2FyKQpsaWJyYXJ5KHRzZXJpZXMpCnJtKGxpc3Q9bHMoKSkKYGBgCgpWICpobGF2bm9tIG1lbnUqIChTZXNzaW9uKSBtw6FtIHByYWNvdm7DvSBhZHJlc8OhciBuYXN0YXZlbsO9IG5hICpTb3VyY2UgRmlsZSBMb2NhdGlvbiouIEFsdGVybmF0w612bmUgamUgbW/Fvm7DqSBwb3XFvmnFpToKCmBgYHtyfQojIHNldHdkKCIvQ2xvdWQvcHJvamVjdC90eXpkbmUvdHl6ZGVuNSIpCmBgYAoKw5pkYWplIHPDuiB2IENTViwgc3TEunBjZSBzw7ogb2RkZWxlbsOpICoqYm9ka2/EjWlhcmtvdSoqIChgO2ApIGEgxI3DrXNsYSBwb3XFvsOtdmFqw7ogKipkZXNhdGlubsO6IMSNaWFya3UqKi4gViBwcm9qZWt0ZSBtw6FtIHBvZHByaWXEjWlub2sgKip1ZGFqZSoqLCBrZGUgamUgc8O6Ym9yIGBkYXRhX2Vrb25vbWV0cmlhLmNzdmAuCgpOaWUgdsWhZXRreSBwcmVtZW5uw6kgcG91xb5pamVtZSDigJMgdnliZXJpZW1lIHRpZSwga3RvcsOpIGTDoXZhasO6IGVrb25vbWV0cmlja8O9IHpteXNlbCBrIHZ5c3ZldGxlbml1IGZpbG1vdsOpaG8gaG9kbm90ZW5pYSBuYSBJTURCLgoKIyDDmnZvZCBkbyBwcm9ibMOpbXUgYSBoeXBvdMOpenkKCkJ1ZGVtZSBtb2RlbG92YcWlICoqSU1EYiBob2Rub3RlbmllKiogdiB6w6F2aXNsb3N0aSBvZDoKLSAqKlJvenBvxI10dSoqIGZpbG11IChvxI1ha8OhdmFtZSAqKmtsYWRuw70qKiB2cGx5diDigJMgdnnFocWhw60gcm96cG/EjWV0ID0gbGVwxaFpYSBwcm9kdWvEjW7DoSBob2Rub3RhIOKGkiBwb3RlbmNpw6FsbmUgdnnFocWhaWUgaG9kbm90ZW5pZSksCi0gKipExLrFvmt5IChtaW4pKiogKG/EjWFrw6F2YW1lICoqbWllcm55IGtsYWRuw70qKiB2cGx5diDigJMgZGxoxaFpZSBmaWxteSBtw7TFvnUgbWHFpSBrb21wbGV4bmVqxaHDrSBwcsOtYmVoLCBubyBlZmVrdCBtw7TFvmUgYnnFpSBhaiBudWxvdsO9KSwKLSAqKlJva3UqKiAoa29udHJvbGEgemEgxI1hc292w70gdHJlbmQ7IG5pZSBqZSB2b3ByZWQgamFzbsOpIHpuYW1pZW5rbyDigJMgcHJlZmVyZW5jaWUgZGl2w6Frb3YgYSDFoXRhbmRhcmR5IHNhIG1lbmlhKSwKLSAqKkRyw6FtYSoqIChpbmRpa8OhdG9yLCDEjWkgZmlsbSBvYnNhaHVqZSDFvsOhbmVyICpEcsOhbWEqOyBoeXBvdMOpemEgKiprbGFkbsO9KiogdnBseXYsIHByZXRvxb5lIGRyYW1hdGlja8OpIGZpbG15IMSNYXN0byBkb21pbnVqw7ogcmVicsOtxI1rb20ga3ZhbGl0eSkuCgpGb3Jtw6FsbmU6ClxcWwpcXHRleHR7SU1EYn0gPSBcXGJldGFfMCArIFxcYmV0YV8xIFxcbG9nKFxcdGV4dHtSb3pwb8SNZXR9KSArIFxcYmV0YV8yIFxcdGV4dHtExLrFvmthfSArIFxcYmV0YV8zIFxcdGV4dHtSb2t9ICsgXFxiZXRhXzQgXFx0ZXh0e0Ryw6FtYX0gKyBcXHZhcmVwc2lsb24KXFxdCgojIFByw61wcmF2YSBkYXRhYsOhenksIMSNaXN0ZW5pZSBhIMO6cHJhdmEgw7pkYWpvdgoKLSBuYcSNw610YW1lIENTViAob2RkZcS+b3ZhxI0gYDtgKSwgIAotIHNwcmF2w61tZSBiZXpwZcSNbsOpIG7DoXp2eSBzdMS6cGNvdiwgIAotIHByZXR5cHVqZW1lICoqSU1EYiBob2Rub3RlbmllKiogKHog4oCeOSwz4oCcIG5hIDkuMyksICoqRMS6xb5rYSAobWluKSoqIGEgKipSb2sqKiBuYSDEjcOtc2xhLCAgCi0geiAqKlJvenBvxI1ldCBbJF0qKiBvZHN0csOhbmltZSBtZWR6ZXJ5IGEgaW7DqSB6bmFreSwgcHJla29udmVydHVqZW1lIG5hIMSNw61zbG8gYSBwb3XFvmlqZW1lIGBsb2cxcGAsICAKLSB6ICoqxb3DoW5lcioqIHZ5dHZvcsOtbWUgaW5kaWvDoXRvciAqKkRyw6FtYSoqIChUUlVFL0ZBTFNFKSwgIAotIGNow71iYWrDumNlIGhvZG5vdHkgdiBudW1lcmlja8O9Y2ggcHJlZGlrdG9yb2NoIG5haHJhZMOtbWUgbWVkacOhbm9tIGRhbmVqIHByZW1lbm5laiAoaWJhIHByZSBtb2RlbG92YW5pZSkuCgpgYGB7cn0KIyA9PT09PSAxKSBOYcSNw610YW5pZSA9PT09PQpyYXcgPC0gcmVhZC5jc3YoImRhdGFfZWtvbm9tZXRyaWEuY3N2IiwKICAgICAgICAgICAgICAgIHNlcCA9ICI7IiwgaGVhZGVyID0gVFJVRSwgc3RyaW5nc0FzRmFjdG9ycyA9IEZBTFNFLAogICAgICAgICAgICAgICAgZmlsZUVuY29kaW5nID0gIlVURi04IikKCmxpYnJhcnkoZHBseXIpCmxpYnJhcnkoc3RyaW5ncikKCiMgUG9tb2Nuw6EgZnVua2NpYSBuYSBzcG/EvmFobGl2w6kgdnnEjWlzdGVuaWUgcm96cG/EjXR1IC0+IG51bWVyaWMKY2xlYW5fYnVkZ2V0IDwtIGZ1bmN0aW9uKHgpIHsKICB4IDwtIGFzLmNoYXJhY3Rlcih4KQogIHggPC0gc3RyX3NxdWlzaCh4KSAgICAgICAgICAgICAgICAgIyBvcmV6YW5pZSB2bsO6dG9ybsO9Y2ggbWVkemllciBuYSAxCiAgeCA8LSBnc3ViKCJcXHMiLCAiIiwgeCkgICAgICAgICAgICAjIG9kc3Ryw6HFiCB2xaFldGt5IG1lZHplcnkKICB4IDwtIGdzdWIoIiwiLCAiIiwgeCkgICAgICAgICAgICAgICMgdGlzw61ja3kgcyDEjWlhcmtvdQogIHggPC0gZ3N1YigiXFwuIiwgIiIsIHgpICAgICAgICAgICAgIyB0aXPDrWNreSBzIGJvZGtvdQogIHggPC0gZ3N1YigiXFwkIiwgIiIsIHgpICAgICAgICAgICAgIyBkb2zDoXIKICB4IDwtIGdzdWIoIlteMC05XSIsICIiLCB4KSAgICAgICAgICMgxI1va2/EvnZlayBuZcSNw61zZWxuw6kgcHJlxI0KICB4W3ggPT0gIiJdIDwtIE5BICAgICAgICAgICAgICAgICAgICMgcHLDoXpkbnkgcmXFpWF6ZWMgLT4gTkEKICBzdXBwcmVzc1dhcm5pbmdzKGFzLm51bWVyaWMoeCkpICAgICMgbmEgxI3DrXNsbwp9CgojID09PT09IDIpIE1hcG92YW5pZSBzdMS6cGNvdiBwb2TEvmEgVHZvamljaCBuw6F6dm92ICsga29udmVyemllID09PT09CmRmIDwtIHJhdyAlPiUKICB0cmFuc211dGUoCiAgICBJTURiICAgICAgID0gc3VwcHJlc3NXYXJuaW5ncyhhcy5udW1lcmljKHN1YigiLCIsICIuIiwgYElNRGIuaG9kbm90ZW5pZWApKSksCiAgICBEbHprYV9taW4gID0gc3VwcHJlc3NXYXJuaW5ncyhhcy5udW1lcmljKGBExLrFvmthLi5taW4uYCkpLAogICAgUm9rICAgICAgICA9IHN1cHByZXNzV2FybmluZ3MoYXMubnVtZXJpYyhSb2spKSwKICAgIFJvenBvY2V0ICAgPSBjbGVhbl9idWRnZXQoYFJvenBvxI1ldC4uLi5gKSwKICAgIFphbnJlICAgICAgPSBgxb3DoW5lcmAsCiAgICBEcmFtYSAgICAgID0gc3RyX2RldGVjdChgxb3DoW5lcmAsIHJlZ2V4KCJEclthw6FdbWEiLCBpZ25vcmVfY2FzZSA9IFRSVUUpKSwKICAgIE5hem92ICAgICAgPSBgTsOhem92YAogICkKCiMgPT09PT0gMykgQmV6cGXEjW5vc3Ruw6Ega29udHJvbGEgdHlwb3YgKGRlYnVnIHXFvml0b8SNbsO9IHbDvXBpcykgPT09PT0KIyBwcmludChzdHIoZGYkUm96cG9jZXQpKSAgIyBtYWxvIGJ5IHVrw6F6YcWlIG51bSAuLi4Kc3RvcGlmbm90KGlzLm51bWVyaWMoZGYkUm96cG9jZXQpKQpzdG9waWZub3QoaXMubnVtZXJpYyhkZiRJTURiKSkKc3RvcGlmbm90KGlzLm51bWVyaWMoZGYkRGx6a2FfbWluKSkKc3RvcGlmbm90KGlzLm51bWVyaWMoZGYkUm9rKSkKCiMgPT09PT0gNCkgSW1wdXTDoWNpYSBudW1lcmlja8O9Y2ggcHJlZGlrdG9yb3YgbWVkacOhbm9tID09PT09Cm51bV9jb2xzIDwtIGMoIkRsemthX21pbiIsICJSb2siLCAiUm96cG9jZXQiKQpmb3IgKGNjIGluIG51bV9jb2xzKSB7CiAgbWVkIDwtIG1lZGlhbihkZltbY2NdXSwgbmEucm0gPSBUUlVFKQogIGRmW1tjY11dW2lzLm5hKGRmW1tjY11dKV0gPC0gbWVkCn0KCiMgSU1EYiBwb25lY2hhaiBsZW4gcyBwbGF0bsO9bWkgaG9kbm90YW1pCmRmIDwtIGRmICU+JSBmaWx0ZXIoIWlzLm5hKElNRGIpKQoKIyA9PT09PSA1KSBMb2ctdHJhbnNmb3Jtw6FjaWEgcm96cG/EjXR1ICgxKyBwcmUgcHLDrXBhZCBudWx5KSA9PT09PQpkZiA8LSBkZiAlPiUgbXV0YXRlKGxvZ19yb3pwb2NldCA9IGxvZzFwKFJvenBvY2V0KSkKCnN1bW1hcnkoZGYpCmBgYAoKVGVyYXogc2kgcG96cmltZSByb3pkZWxlbmllIGvEvsO6xI1vdsO9Y2ggcHJlbWVubsO9Y2ggYSBwb3RlbmNpw6FsbmUgb2TEvmFobMOpIGhvZG5vdHk6CgpgYGB7cn0KcGFyKG1mcm93PWMoMiwyKSwgbWFyPWMoNCw0LDIsMSkpCmJveHBsb3QoZGYkSU1EYiwgbWFpbj0iSU1EYiBob2Rub3RlbmllIiwgeGxhYj0iSG9kbm90YSIpCmJveHBsb3QoZGYkRGx6a2FfbWluLCBtYWluPSJExLrFvmthIChtaW4pIiwgeGxhYj0iTWluw7p0eSIpCmJveHBsb3QoZGYkUm96cG9jZXQsIG1haW49IlJvenBvxI1ldCBbJF0iLCB4bGFiPSJVU0QiKQpib3hwbG90KGRmJGxvZ19yb3pwb2NldCwgbWFpbj0ibG9nKDErUm96cG/EjWV0KSIsIHhsYWI9ImxvZyBVU0QiKQpwYXIobWZyb3c9YygxLDEpKQpgYGAKCiMgTGluZcOhcm5hIHJlZ3Jlc2lhCgpPZGhhZG5lbWUgbW9kZWw6CgpgYGB7cn0KbW9kZWwgPC0gbG0oSU1EYiB+IDEgKyBsb2dfcm96cG9jZXQgKyBEbHprYV9taW4gKyBSb2sgKyBEcmFtYSwgZGF0YT1kZikKc3VtbWFyeShtb2RlbCkKYGBgCgojIyMgxIxvIG9iamVrdCBgbG0oKWAgb2JzYWh1amUKCjEuIE9kaGFkbnV0w6kga29lZmljaWVudHk6IGBtb2RlbCRjb2VmZmljaWVudHNgICAKMi4gUmV6aWR1w6E6IGBtb2RlbCRyZXNpZHVhbHNgICAKMy4gVnlyb3ZuYW7DqSBob2Rub3R5OiBgbW9kZWwkZml0dGVkLnZhbHVlc2AgIAo0LiBEaXpham5vdsO6IG1hdGljdSBYOiBgbW9kZWwubWF0cml4KG1vZGVsKWAKCmBgYHtyfQojIHByw61rbGFkeQptb2RlbCRjb2VmZmljaWVudHMKaGVhZChtb2RlbCRyZXNpZHVhbHMpCmhlYWQobW9kZWwkZml0dGVkLnZhbHVlcykKZGltKG1vZGVsLm1hdHJpeChtb2RlbCkpCmBgYAoKIyMjIERpYWdub3N0aWNrw6kgZ3JhZnkKCmBgYHtyfQpwYXIobWZyb3cgPSBjKDIsIDIpKQpwbG90KG1vZGVsKQpwYXIobWZyb3cgPSBjKDEsIDEpKQpgYGAKCiMjIFJlc2lkdWFscyB2cy4gRml0dGVkIOKAkyBpbnRlcnByZXTDoWNpYSBrIG5hxaFlaiBkYXRhYsOhemUKLSAqKkNlbnRyb3ZhbmllIG9rb2xvIG51bHk6KiogUmV6aWR1w6Egb3NjaWx1asO6IG9rb2xvIG51bHkg4oaSIG1vZGVsIG5pZSBqZSBzeXN0ZW1hdGlja3kgc2tyZXNsZW7DvS4gIAotICoqVmFyaWFuY2lhIHJlesOtZHXDrToqKiBBayByb3pwdHlsICh2ZXJ0aWvDoWxuZSDigJ5tcmFreeKAnCkgcmFzdGllIHMgZml0dGVkIGhvZG5vdGFtaSwgaWRlIG8gaGV0ZXJvc2tlZGFzdGljaXR1OyBpbmFrIGplIHRvIGZham4uICAKLSAqKk5lbGluZWFyaXR5OioqIEFrIHNhIMSNZXJ2ZW7DoSBMT0VTUyDEjWlhcmEgdsO9cmF6bmUga3JpdsOtLCBtw7TFvmUgY2jDvWJhxaUgbmVsaW5lw6FybnkgdHZhciAobmFwci4gdHJhbnNmb3Jtw6FjaWEgZMS6xb5reSBhbGVibyBpbnRlcmFrY2lhIMW+w6FucnUpLiAgCi0gKipPZMS+YWhsw6kgaG9kbm90eToqKiBGaWxteSBzIGV4dHLDqW1uZSB2eXNva8O9bSByb3pwb8SNdG9tIG3DtMW+dSBiecWlIHZwbHl2bsOpIOKAkyBza29udHJvbHVqZW1lIMSPYWxlai4KCiMjIFHigJNRIHBsb3QKLSBCb2R5IGJsw616a28gcHJpYW1reSDih5IgcmV6aWR1w6EgcHJpYmxpxb5uZSBub3Jtw6FsbmUuICAKLSBPZGNow71sa3kgdiBrb25jb2NoIOKHkiDFpWHFvsWhaWUgY2h2b3N0eSAocMOhciBmaWxtb3YgcyBleHRyw6ltbnltaSBob2Rub3RhbWkpLiAgCgojIyBTY2FsZeKAk0xvY2F0aW9uCi0gUGxvY2jDoSBMT0VTUyBrcml2a2Eg4oeSIHByaWJsacW+bmUga29uxaF0YW50bsOhIHZhcmlhbmNpYSByZXrDrWR1w60gKGhvbW9za2VkYXN0aWNpdGEpLiAgCi0gVnpvcmVjIOKAnmxpZXZpa2HigJ0g4oeSIGhldGVyb3NrZWRhc3RpY2l0YSAoxI1hc3RvIHByaSBmaW5hbsSNbsO9Y2ggcHJlbWVubsO9Y2ggYWtvIHJvenBvxI1ldCkuCgojIyBSZXNpZHVhbHMgdnMuIExldmVyYWdlCi0gUG96b3IgbmEgYm9keSBzIHZ5c29rb3UgcMOha291ICh2ZcS+bWkgb2RsacWhbsOpIHJvenBvxI10eS9kxLrFvmt5KSBhIHZ5c29rw71taSBDb29rb3bDvW1pIHZ6ZGlhbGVub3PFpWFtaS4gIAotIFZlxL5taSB2cGx5dm7DqSBwb3pvcm92YW5pYSBtw7TFvnUgbmXDum1lcm5lIMWlYWhhxaUga29lZmljaWVudHkuCgojIyMgVGVzdHkgbm9ybWFsaXR5IGEgb2TEvmFobG9zdMOtCgpgYGB7cn0KcmVzaWQgPC0gcmVzaWR1YWxzKG1vZGVsKQpqYXJxdWUuYmVyYS50ZXN0KHJlc2lkKQoKIyBwb3RlbmNpb27DoWxuZSBvZMS+YWhsw6kvdnBseXZuw6kgcG96b3JvdmFuaWEgKEJvbmZlcnJvbmkgcC1ob2Rub3R5KQpvdXRsaWVyVGVzdChtb2RlbCkKYGBgCgpLZcSPxb5lIHJvenBvxI10eSBtw6F2YWrDuiBzaWxuw6kgb2TEvmFobMOpIGhvZG5vdHksIGJlxb5uZSBwb23DoWhhIHByw6F2ZSBsb2ctdHJhbnNmb3Jtw6FjaWEgKGt0b3LDuiB1xb4gcG91xb7DrXZhbWUpLiBQcmUga29udHJvbHUgcm9idXN0bm9zdGkgbcO0xb5lbWUgc2vDunNpxaUgemplZG5vZHXFoWVuw70gbW9kZWwgYmV6IHJva3UgYWxlYm8gcyBpbsO9bSBrw7Nkb3ZhbsOtbSDFvsOhbnJ1LgoKYGBge3J9CiMgYWx0ZXJuYXTDrXZhOiBiZXogUm9rYQptb2RlbDIgPC0gbG0oSU1EYiB+IDEgKyBsb2dfcm96cG9jZXQgKyBEbHprYV9taW4gKyBEcmFtYSwgZGF0YT1kZikKc3VtbWFyeShtb2RlbDIpCgpwYXIobWZyb3c9YygyLDIpKTsgcGxvdChtb2RlbDIpOyBwYXIobWZyb3c9YygxLDEpKQoKCmphcnF1ZS5iZXJhLnRlc3QocmVzaWR1YWxzKG1vZGVsMikpCm91dGxpZXJUZXN0KG1vZGVsMikKYGBgCgojIFpocm51dGllIChDb25jbHVzaW9uKQoKLSBWIG5hxaFlaiAqKmZpbG1vdmVqIGRhdGFiw6F6ZSoqIChJTURCIHJlYnLDrcSNZWspIHZ5Y2jDoWR6YSwgxb5lOgogIC0gKipSb3pwb8SNZXQqKiAodiBsb2dhcml0bWUpIG3DoSAqKmtsYWRuw70gYSDFoXRhdGlzdGlja3kgdsO9em5hbW55KiogdnrFpWFoIHMgKipJTURiIGhvZG5vdGVuw61tKiog4oCTIHbDpMSNxaFpZSByb3pwb8SNdHkgc8O6IHNwb2plbsOpIHMgbWllcm5lIHZ5xaHFocOtbWkgc2vDs3JlIChwbyBrb250cm9sw6FjaCkuCiAgLSAqKkTEusW+a2EgKG1pbikqKiBtw6Egb2J2eWtsZSAqKm1hbMO9IHBveml0w612bnkqKiBhbGVibyAqKm5ldsO9em5hbW55KiogZWZla3QgKHrDoXZpc8OtIG9kIHZ6b3JreSkuCiAgLSAqKlJvayoqIG3DtMW+ZSB6YWNoeXTDoXZhxaUgdHJlbmQgxI1pIGtvaG9ydG7DqSBlZmVrdHkgKGFrIGplIHbDvXpuYW1uw70sIHNtZXIgYSB2ZcS+a29zxaUgdHJlYmEgaW50ZXJwcmV0b3ZhxaUgb3BhdHJuZSkuCiAgLSBGaWxteSBzbyDFvsOhbnJvbSAqKkRyw6FtYSoqIG3DoXZhasO6IHR5cGlja3kgKip2ecWhxaFpZSBob2Rub3RlbmllKiogKGluZGlrw6F0b3IgKkRyYW1hID0gVFJVRSogdnljaMOhZHphIMSNYXN0byBrbGFkbmUpLgotIERpYWdub3N0aWt5IHbDpMSNxaFpbm91IG5ldWthenVqw7ogesOhc2FkbsOpIG5lbGluZWFyaXR5LiBPZGNow71sa3kgb2Qgbm9ybWFsaXR5IHYgY2h2b3N0b2NoIHPDuiBwcmkgZmlsbW92w71jaCBkw6F0YWNoIGJlxb5uw6kgKGV4dHLDqW1uZSByb3pwb8SNdHkvaGl0eSkuIFByaSB2w6TEjcWhb20gTiBqZSBPTFMgYWogdGFrIHNwb8S+YWhsaXbDvTsgcHLDrXBhZG5lIHNhIGTDoSBwb3XFvmnFpSAqKnJvYnVzdG7DoSBrb3ZhcmlhbsSNbsOhIG1hdGljYSoqIChIQyk6CgpgYGB7cn0KIyByb2J1c3Ruw6kgKEhDKSBzbWVyb2Rham7DqSBjaHlieSBwcmUgbW9kZWwKY29lZnRlc3QobW9kZWwsIHZjb3YgPSBzYW5kd2ljaDo6dmNvdkhDKG1vZGVsLCB0eXBlID0gIkhDMSIpKQpgYGAKCiMgSGV0ZXJvc2tlZGFzdGljaXRhClByw610b21ub3PFpSBoZXRlcm9za2VkYXN0aWNpdHkgKG5la29uxaF0YW50bsOpaG8gcm96cHR5bHUgbsOhaG9kbmVqIHpsb8W+a3kpIHNww7Rzb2J1amUgbmVzcG/EvmFobGl2w6kgdC10ZXN0eSBwcmkgYmXFvm7DvWNoIChPTFMpIHNtZXJvZGFqbsO9Y2ggY2h5YsOhY2guIFByZXRvIGp1OgoKICAtbmFqcHJ2IHZpenXDoWxuZS90ZXN0YW1pIGRldGVndWplbWUsCgogIC1hIGFrIHNhIHZ5c2t5dHVqZSwgcG91xb5pamVtZSByb2J1c3Ruw6kgKEhDKSBzbWVyb2Rham7DqSBjaHlieSBhbGVibyB2aG9kbsO6IHRyYW5zZm9ybcOhY2l1IChuYXByLiBsb2cgcm96cG/EjXR1KS4KCk5pxb7FoWllIHZpenVhbGl6dWplbWUgdnrFpWFoIMWhdHZvcmNvdiByZXrDrWR1w60gYSB2eWJyYW7DvWNoIHZ5c3ZldMS+dWrDumNpY2ggcHJlbWVubsO9Y2guIFNrw7ptYW1lIG9iYSBvZGhhZG51dMOpIG1vZGVseToKbW9kZWwgKElNRGIgfiBsb2dfcm96cG9jZXQgKyBEbHprYV9taW4gKyBSb2sgKyBEcmFtYSkgYSBtb2RlbDIgKG5hcHIuIGJleiBSb2thKS4KYGBge3J9CmxpYnJhcnkoZ2dwbG90MikKbGlicmFyeShwYXRjaHdvcmspIAoKIyByZXppZHXDoSBwcmUgbW9kZWwKCmRmX3JlczEgPC0gZGYKZGZfcmVzMSRyZXMyIDwtIHJlc2lkdWFscyhtb2RlbCleMgoKcDEgPC0gZ2dwbG90KGRmX3JlczEsIGFlcyh4ID0gbG9nX3JvenBvY2V0LCB5ID0gcmVzMikpICsKZ2VvbV9wb2ludChhbHBoYSA9IDAuNikgKwpnZW9tX3Ntb290aChtZXRob2QgPSAibG9lc3MiLCBzZSA9IEZBTFNFKSArCmxhYnMoeCA9ICJsb2coMSArIFJvenBvxI1ldCkiLCB5ID0gIsWgdHZvcmNlIHJlesOtZHXDrSIsCnRpdGxlID0gIsWgdHZvcmNlIHJlesOtZHXDrSB2cy4gbG9nKHJvenBvxI1ldCkg4oCTIG1vZGVsIikgKwp0aGVtZV9taW5pbWFsKCkKCnAyIDwtIGdncGxvdChkZl9yZXMxLCBhZXMoeCA9IERsemthX21pbiwgeSA9IHJlczIpKSArCmdlb21fcG9pbnQoYWxwaGEgPSAwLjYpICsKZ2VvbV9zbW9vdGgobWV0aG9kID0gImxvZXNzIiwgc2UgPSBGQUxTRSkgKwpsYWJzKHggPSAiRMS6xb5rYSAobWluKSIsIHkgPSAixaB0dm9yY2UgcmV6w61kdcOtIiwKdGl0bGUgPSAixaB0dm9yY2UgcmV6w61kdcOtIHZzLiBkxLrFvmthIOKAkyBtb2RlbCIpICsKdGhlbWVfbWluaW1hbCgpCgpwMSArIHAyCmBgYApgYGB7cn0KIyByZXppZHXDoSBwcmUgbW9kZWwyCgpkZl9yZXMyIDwtIGRmCmRmX3JlczIkcmVzMiA8LSByZXNpZHVhbHMobW9kZWwyKV4yCgpwMWIgPC0gZ2dwbG90KGRmX3JlczIsIGFlcyh4ID0gbG9nX3JvenBvY2V0LCB5ID0gcmVzMikpICsKZ2VvbV9wb2ludChhbHBoYSA9IDAuNikgKwpnZW9tX3Ntb290aChtZXRob2QgPSAibG9lc3MiLCBzZSA9IEZBTFNFKSArCmxhYnMoeCA9ICJsb2coMSArIFJvenBvxI1ldCkiLCB5ID0gIsWgdHZvcmNlIHJlesOtZHXDrSIsCnRpdGxlID0gIsWgdHZvcmNlIHJlesOtZHXDrSB2cy4gbG9nKHJvenBvxI1ldCkg4oCTIG1vZGVsMiIpICsKdGhlbWVfbWluaW1hbCgpCgpwMmIgPC0gZ2dwbG90KGRmX3JlczIsIGFlcyh4ID0gRGx6a2FfbWluLCB5ID0gcmVzMikpICsKZ2VvbV9wb2ludChhbHBoYSA9IDAuNikgKwpnZW9tX3Ntb290aChtZXRob2QgPSAibG9lc3MiLCBzZSA9IEZBTFNFKSArCmxhYnMoeCA9ICJExLrFvmthIChtaW4pIiwgeSA9ICLFoHR2b3JjZSByZXrDrWR1w60iLAp0aXRsZSA9ICLFoHR2b3JjZSByZXrDrWR1w60gdnMuIGTEusW+a2Eg4oCTIG1vZGVsMiIpICsKdGhlbWVfbWluaW1hbCgpCgpwMWIgKyBwMmIKYGBgClogdnlobGFkZW5pYSAoTE9FU1MpIHBvc8O6ZGltZSwgxI1pIHNhIHJvenB0eWwgcmV6w61kdcOtIHN5c3RlbWF0aWNreSBtZW7DrSBzIHZ5c3ZldMS+dWrDumNpbWkgcHJlbWVubsO9bWkgKG5hcHIuIOKAnmxpZXZpa+KAnCkuCgojIyBUZXN0b3ZhbmllIHByw610b21ub3N0aSBoZXRlcm9za2VkYXN0aWNpdHkKYGBge3J9CmxpYnJhcnkobG10ZXN0KQoKIyBCcmV1c2No4oCTUGFnYW4gdGVzdCBwcmUgb2JhIG1vZGVseQoKYnAxIDwtIGJwdGVzdChtb2RlbCkKYnAyIDwtIGJwdGVzdChtb2RlbDIpCgpicDEKYnAyCmBgYApBayB0ZXN0IGluZGlrdWplIGhldGVyb3NrZWRhc3RpY2l0dSAobWFsw6kgcC1ob2Rub3R5KSwgcG91xb5pamVtZSByb2J1c3Ruw6kgKEhDKSBzbWVyb2Rham7DqSBjaHlieToKYGBge3J9CmxpYnJhcnkoc2FuZHdpY2gpCmxpYnJhcnkobG10ZXN0KQoKIyBSb2J1c3Ruw6kgKEhDMSkgc21lcm9kYWpuw6kgY2h5Ynkg4oCTIG9kcG9yw7rEjWFuw6kgcmVwb3J0b3ZhxaUgcyBPTFMga29lZmljaWVudG1pCgpjb2VmdGVzdChtb2RlbCwgIHZjb3YgPSB2Y292SEMobW9kZWwsICB0eXBlID0gIkhDMSIpKQpjb2VmdGVzdChtb2RlbDIsIHZjb3YgPSB2Y292SEMobW9kZWwyLCB0eXBlID0gIkhDMSIpKQoKYGBgCgojIyBQb3puw6Fta2EKClByaSBmaW5hbsSNbsO9Y2ggcHJlbWVubsO9Y2ggKGFrbyByb3pwb8SNZXQpIGLDvXZhIGhldGVyb3NrZWRhc3RpY2l0YSDEjWFzdMOhOyBsb2ctdHJhbnNmb3Jtw6FjaWEgcm96cG/EjXR1ICjEjW8gdcW+IHYgbW9kZWxpIHBvdcW+w612YW1lKSBqZSBzcHLDoXZueSBrcm9rLgpSb2J1c3RubsOpIFNFIChIQykgc8O6IHZob2Ruw6kgbmFqbcOkIHByaSB2w6TEjcWhw61jaCB2em9ya8OhY2guCkFsdGVybmF0w612b3UgamUgxaFwZWNpZmlrb3ZhxaUgbW9kZWwgaW5hayAobmFwci4gaW50ZXJha2NpZSwgdHJhbnNmb3Jtw6FjaWUpLCBhbGUgcm9idXN0bsOpIGNoeWJ5IHPDuiBuYWpqZWRub2R1Y2jFoWllIGEgxI1hc3RvIHBvc3RhxI11asO6Y2UgcmllxaFlbmllLg==