knitr::opts_chunk$set(
    echo = TRUE,
    message = FALSE,
    warning = FALSE
)

Práca s údajmi

Tradičná práca s databázou

Pre prácu s údajmi (databázou) používame najčastejšie dátový typ .data.frame.. Je to tabuľka, ktorá pozostáva zo stĺpcov rozličných typov. Jeden riadok pritom predstavuje jeden záznam databázy.

Príklad

Majme údaje o žiakoch, ktoré predstavujú tri premenné - Meno, Vek a Body:

# Working with data frames

  Meno = c("Katarína", "Ján", "Alexander")
  Vek = c(24, 20, 21)
  Body = c(87, 96, 75)

Tieto tri premenné nie sú zatiaľ nijako prepojené, predstavujú izolované stĺpce tabuľky. Do tabuľky ich spojíme nasledovne

udaje <- data.frame(Meno,Vek,Body)
print(udaje)

Vysvetlenie: DataFrame má tri stĺpce: Meno, Vek a Body. Niektoré operácie s údajmi organizovanými v .data.frame. sú uvedené nasledovne

print(udaje$Vek)                 
[1] 24 20 21
print(mean(udaje$Vek))           
[1] 21.66667
print(udaje[Meno=="Jozef",])     
print(udaje[3,])                 
print(udaje[,2:3])               
print(udaje[1,1])                
[1] "Katarína"
summary(udaje)                   
     Meno                Vek             Body     
 Length:3           Min.   :20.00   Min.   :75.0  
 Class :character   1st Qu.:20.50   1st Qu.:81.0  
 Mode  :character   Median :21.00   Median :87.0  
                    Mean   :21.67   Mean   :86.0  
                    3rd Qu.:22.50   3rd Qu.:91.5  
                    Max.   :24.00   Max.   :96.0  

Ak chceme pridať k tabuľke dodatočný stĺpec, potom to robíme nasledovne

MaAuto <- c(TRUE,FALSE,TRUE)
udaje <- cbind(udaje,MaAuto)
print(udaje)

Ak chceme pridať riadok, potom

# New record (must match column order/types)
novy.riadok <- data.frame(Meno = "Viktória", Vek = 16, Body = 89,MaAuto = FALSE)

# Append
udaje <- rbind(udaje, novy.riadok)
print(udaje)

Tabuľky v prostredí kableextra

library(knitr)
library(kableExtra)
kable(
  udaje,
#  format,
digits = 2,
#  row.names = NA,
#  col.names = NA,
  align=c("l","c","l","r"),
  caption = "Toto je tabuľka"
#  label = NULL,
#  format.args = list(),
#  escape = TRUE,
 # ...
) %>%
      kable_styling(bootstrap_options = c("striped", "hover", "condensed", "responsive"),
    full_width = FALSE,
    position = "center")
Toto je tabuľka
Meno Vek Body MaAuto
Katarína 24 87 TRUE
Ján 20 96 FALSE
Alexander 21 75 TRUE
Viktória 16 89 FALSE
NA
NA
NA
NA
NA

Tidyverse - moderná práca s údajmi

Tidyverse je súbor knižníc, ktoré majú zjednodušiť prácu s údajmi. Majú jednotný komunikačný štandard, vzájomne sa doplňujú.

# Load tidyverse
library(tidyverse)

dplyr - pre manipuláciu s údajmi

.dplyr. poskytuje základné možnosti manipulácie s údajmi, ako napr.:

  1. filter(): vyberá riadky

  2. select(): vyberá stĺpce

  3. mutate(): vytvára nové stĺpce tabuľky

  4. arrange(): triedi riadky

  5. summarise(): sumarizuje

V nasledovnej ukážke využijeme tzv. .pipes. %>% alebo %<% umožňuje posielať výsledky z jednej funkcie priamo do volanie nasledovnej funkcie. To umožňuje ľahšiu čitateľnosť kódov, konvencia sa ujala a má široké použitie.

Výber a triedenie

# výber a následné triedenie
udaje %>%
  filter(Body > 62) %>%     
  arrange(desc(Body)) %>%     
kable %>%
    kable_styling(
    bootstrap_options = c("striped", "hover", "condensed", "responsive"),
    full_width = FALSE,
    position = "center"
  )
Meno Vek Body MaAuto
Ján 20 96 FALSE
Viktória 16 89 FALSE
Katarína 24 87 TRUE
Alexander 21 75 TRUE

Zoskupenie a sumarizácia

# Zoskupí and sumarizuje
udaje %>%
  group_by(MaAuto) %>%      # zoskupi zaznamy podla premennej MaAuto a vypocita za kazdu skupinu jej priemer Body
  summarise(                # a taktiez spocita pocetnosti oboch skupin
    Priem.Body = mean(Body),
    count = n()
  ) %>%
 kable(
    caption = "Priemerné Body podľa premennej MaAuto",
    col.names = c("Má Auto", "Priemer Body", "Počet"),
    align = "c"
  ) %>%
  kable_styling(
    bootstrap_options = c("striped", "hover", "condensed", "responsive"),
    full_width = FALSE,
    position = "center"
  )
Priemerné Body podľa premennej MaAuto
Má Auto Priemer Body Počet
FALSE 92.5 2
TRUE 81.0 2

Vytváranie novej premennej

# Vytváranie novej premennej
udaje %>%
  mutate(
    grade = case_when(     # vytvara novu premennu grade podla nasledovnej relacnej schemy
      Body >= 90 ~ "A",
      Body >= 80 ~ "B",
      Body >= 70 ~ "C",
      TRUE ~ "D"
    ),
    VekPoPlnoletosti = round(Vek-18,0)
  ) %>% 
  kable %>%
   kable_styling(
    bootstrap_options = c("striped", "hover", "condensed", "responsive"),
    full_width = FALSE,
    position = "center"
  ) 
Meno Vek Body MaAuto grade VekPoPlnoletosti
Katarína 24 87 TRUE B 6
Ján 20 96 FALSE A 2
Alexander 21 75 TRUE C 3
Viktória 16 89 FALSE B -2

Import údajov z otv. databáz

  1. Mendeley Data Tuto sa dostaneme z Mendeley Data, kde si údaje viete voľne stiahnúť. Údaje sa vzťahujú k už publikovaným článkom vo vydavateľstve Elsevier. Výber sa dá urobiť jednoducho zadaním kľúčových slov.
  2. Kaggle Data Tuto sa dostaneme z Kaggle Datasets, kde si údaje viete voľne stiahnúť. Údaje sa vzťahujú k projektom podporovaným Kaggle. Výber sa dá urobiť jednoducho zadaním kľúčových slov.
  3. Databázy knižníc R - .library(datasets). alebo .library(wooldridge). ale aj iné - stačí si dať príkaz data()
library(datasets)
# datasets available in the 'datasets' package - nasledovne kody za mna urobil Chat GPT
ds <- as.data.frame(utils::data(package = "datasets")$results)[, c("Item","Title")]
knitr::kable(head(ds, 20), col.names = c("Dataset", "Title"))   # prvych 20 databaz
# kniznica datasets obsahuje databazu nazvanu CO2. Mozeme sa na nu odvolavat nasledovne, ako napr. 
head(CO2)

Môžeme použiť aj databázu určenú pre ekobometriu - package Wooldridge

# install.packages("wooldridge")
library(wooldridge)
ds <- as.data.frame(utils::data(package = "wooldridge")$results)[, c("Item","Title")]
knitr::kable(head(ds, 20), col.names = c("Dataset", "Title")) %>%
    kable_styling(
    bootstrap_options = c("striped", "hover", "condensed", "responsive"),
    full_width = FALSE,
    position = "center"
  )

Import údajov z .csv alebo .xls

Ja som si zvolil údaje z [Abosede Tiamiyu: Environmental, Social, and Governance Reporting Evidencing Firm Performance in Emerging Economy]{https://data.mendeley.com/datasets/7k8pjhsrwb/1}. Na stránke sa nachádza súbor .Dataset ESG and Firm Performance.xlsx., ktorý som si stiahol a exportoval do formátu csv. Ako oddeľovač položiek som si zvolil bodkočiarku (semicolon ;), vyžívam desatinnú bodku a nie čiarku a tiež textové premenné uvádzam apostrofmi “. V prvom riadku sa nachádzajú názvy stĺpcov, ktoré neskôr budú vystupovať ako premenné. Tie obsahujú medzery, čo je v zázve premennej neprípustné a nahradil som ich podtrhovátkom”.”.

Náhľad na xls databázu otvorenú v tabuľkovom procesore
Náhľad na xls databázu otvorenú v tabuľkovom procesore
Náhľad na csv databázu otvorenú v textovom procesore
Náhľad na csv databázu otvorenú v textovom procesore

Potom už stačí importovať údaje do .data.frame., a to nasledovne

udaje <- read.csv2("udaje/Dataset ESG and Firm Performance.csv",header=TRUE,sep=";",dec=".")
head(udaje)                                             # niekolko prvych riadkov
colnames(udaje)                                         # nazvy premennych

Grafy

ggplot2 - knižnica pre grafy

Výber a následné triedenie

library(dplyr)

udaje.2013 <- udaje %>%
  filter(YEARS == 2013) %>%
  select(RETURN.ON.ASSETS, ESG.INDEX, DEBT.TO.ASSET, FIRM.SIZE)

Knižnica .ggplot2. je v súčasnosti najčastejšie používaná grafická knižnica, pričom predpripravené kódy k jednotlivým obrázkom si viete nájsť v R Graph Gallery. Tu si uvedieme jednoduchšie z nich.

Scatter plot

# Basic scatter plot
library(ggplot2)
ggplot(udaje.2013, aes(x = FIRM.SIZE, y = ESG.INDEX)) +            # specifikacia osi
  geom_point() +                                                   # typ grafu - scatterplot
  theme_minimal() +
  labs(title = "ESG index", x = "Veľkosť firmy", y = "Score")      # oznacenie osi

Boxplot

# Bar plot with grouping
library(ggplot2)

library(ggplot2)

ggplot(udaje, aes(x = factor(YEARS), y = ESG.INDEX)) +        # specifikacia osi
  geom_boxplot(fill = "lightblue", color = "darkblue") +      # typ grafu - boxplot
  labs(                                                       # oznacenie osi, nazov grafu
    title = "ESG Index by Years",
    x = "Year",
    y = "ESG Index"
  ) +
  theme_minimal()

Základné štatistiky.

knitr - tabuľka

library(dplyr)
library(knitr)

# Summarise basic statistics
esg.stats <- udaje %>%
  filter(YEARS %in% 2013:2016) %>%
  group_by(YEARS) %>%
  summarise(
    n     = n(),
    mean  = mean(ESG.INDEX, na.rm = TRUE),
    sd    = sd(ESG.INDEX, na.rm = TRUE),
    min   = min(ESG.INDEX, na.rm = TRUE),
    q25   = quantile(ESG.INDEX, 0.25, na.rm = TRUE),
    median= median(ESG.INDEX, na.rm = TRUE),
    q75   = quantile(ESG.INDEX, 0.75, na.rm = TRUE),
    max   = max(ESG.INDEX, na.rm = TRUE),
    .groups = "drop"
  )

# Create knitr table
kable(esg.stats, digits = 2, caption = "Basic statistics of ESG Index (2013–2016)")

alebo krajšie tabuľky s pomocou .kableExtra.:

library(dplyr)
library(knitr)
library(kableExtra)

# Summarise basic statistics
esg.stats <- udaje %>%
  filter(YEARS %in% 2013:2016) %>%
  group_by(YEARS) %>%
  summarise(
    n      = n(),
    mean   = mean(ESG.INDEX, na.rm = TRUE),
    sd     = sd(ESG.INDEX, na.rm = TRUE),
    min    = min(ESG.INDEX, na.rm = TRUE),
    q25    = quantile(ESG.INDEX, 0.25, na.rm = TRUE),
    median = median(ESG.INDEX, na.rm = TRUE),
    q75    = quantile(ESG.INDEX, 0.75, na.rm = TRUE),
    max    = max(ESG.INDEX, na.rm = TRUE),
    .groups = "drop"
  )

# Create styled kableExtra table
esg.stats %>%
  kable(digits = 2, caption = "Basic statistics of ESG Index (2013–2016)") %>%
  kable_styling(full_width = FALSE, bootstrap_options = c("striped", "hover", "condensed")) %>%
  column_spec(1, bold = TRUE) %>%          # make years bold
  row_spec(0, bold = TRUE, background = "#f2f2f2") %>%  # style header row
  add_header_above(c(" " = 2, "ESG Index Statistics" = 7))

t-test: Porovnanie priemeru ESG indexu v rokoch 2013 a 2015

t.test.result <- t.test(
  udaje$ESG.INDEX[udaje$YEARS == 2013],
  udaje$ESG.INDEX[udaje$YEARS == 2015]
)

print(t.test.result)

ANOVA: Comparing Reading Scores Across Programs

anova.result <- aov(ESG.INDEX ~ YEARS, data = udaje)
summary(anova.result)

Linear Regression: Predicting Math Scores

model <- lm(ESG.INDEX ~ RETURN.ON.ASSETS + FIRM.SIZE + DEBT.TO.ASSET, data = udaje.2013)
summary(model)
# install.packages(c("broom", "kableExtra", "dplyr", "stringr"))
library(broom)
library(dplyr)
library(kableExtra)
library(stringr)

# Your model (already fitted)
# model <- lm(ESG.INDEX ~ RETURN.ON.ASSETS + FIRM.SIZE + DEBT.TO.ASSET, data = udaje.2013)

coef.tbl <- tidy(model, conf.int = TRUE) %>%
  mutate(
    term = recode(term,
      "(Intercept)" = "Intercept",
      "RETURN.ON.ASSETS" = "Return on Assets",
      "FIRM.SIZE" = "Firm Size",
      "DEBT.TO.ASSET" = "Debt to Asset"
    ),
    stars = case_when(
      p.value < 0.001 ~ "***",
      p.value < 0.01  ~ "**",
      p.value < 0.05  ~ "*",
      p.value < 0.1   ~ "·",
      TRUE            ~ ""
    )
  ) %>%
  transmute(
    Term = term,
    Estimate = estimate,
    `Std. Error` = std.error,
    `t value` = statistic,
    `p value` = p.value,
    `95% CI` = str_c("[", round(conf.low, 3), ", ", round(conf.high, 3), "]"),
    Sig = stars
  )

coef.tbl %>%
  kable(
    digits = 3,
    caption = "OLS Regression Coefficients (ESG.INDEX ~ RETURN.ON.ASSETS + FIRM.SIZE + DEBT.TO.ASSET)"
  ) %>%
  kable_styling(full_width = FALSE, bootstrap_options = c("striped", "hover", "condensed")) %>%
  column_spec(1, bold = TRUE) %>%
  row_spec(0, bold = TRUE, background = "#f2f2f2") %>%
  footnote(
    general = "Signif. codes: *** p<0.001, ** p<0.01, * p<0.05, · p<0.1.",
    threeparttable = TRUE
  )
fit.tbl <- glance(model) %>%
  transmute(
    `R-squared` = r.squared,
    `Adj. R-squared` = adj.r.squared,
    `F-statistic` = statistic,
    `F p-value` = p.value,
    `AIC` = AIC,
    `BIC` = BIC,
    `Num. obs.` = nobs
  )

fit.tbl %>%
  kable(digits = 3, caption = "Model Fit Statistics") %>%
  kable_styling(full_width = FALSE, bootstrap_options = c("condensed"))

Info zdroje pre ďalšie štúdium

R Project

Posit

Community Resources

LS0tCnRpdGxlOiAiUHLDoWNhIHMgZGF0YWLDoXpvdSIKYXV0aG9yOiAiVmVyb25pa2EgUml6c255b3ZzemvDoSAgPGJyPiIKZGF0ZTogIk9rdMOzYmVyIDIwMjUiCm91dHB1dDogCiAgaHRtbF9ub3RlYm9vazoKICAgIHRvYzogdHJ1ZQogICAgdG9jX2Zsb2F0OiB0cnVlCiAgICB0aGVtZTogdW5pdGVkCiAgICBoaWdobGlnaHQ6IHRhbmdvCmVkaXRvcl9vcHRpb25zOiAKICBtYXJrZG93bjogCiAgICB3cmFwOiA3MgotLS0KCmBgYHtyfQprbml0cjo6b3B0c19jaHVuayRzZXQoCiAgICBlY2hvID0gVFJVRSwKICAgIG1lc3NhZ2UgPSBGQUxTRSwKICAgIHdhcm5pbmcgPSBGQUxTRQopCmBgYAoKCiMgUHLDoWNhIHMgw7pkYWptaQoKIyMgVHJhZGnEjW7DoSBwcsOhY2EgcyBkYXRhYsOhem91CgpQcmUgcHLDoWN1IHMgw7pkYWptaSAoZGF0YWLDoXpvdSkgcG91xb7DrXZhbWUgbmFqxI1hc3RlasWhaWUgZMOhdG92w70gdHlwIC5kYXRhLmZyYW1lLi4gSmUgdG8gdGFidcS+a2EsIGt0b3LDoSBwb3pvc3TDoXZhIHpvIHN0xLpwY292IHJvemxpxI1uw71jaCB0eXBvdi4gSmVkZW4gcmlhZG9rIHByaXRvbSBwcmVkc3RhdnVqZSBqZWRlbiB6w6F6bmFtIGRhdGFiw6F6eS4KCiMjIyBQcsOta2xhZAoKTWFqbWUgw7pkYWplIG8gxb5pYWtvY2gsIGt0b3LDqSBwcmVkc3RhdnVqw7ogdHJpIHByZW1lbm7DqSAtIE1lbm8sIFZlayBhIEJvZHk6CgpgYGB7cn0KIyBXb3JraW5nIHdpdGggZGF0YSBmcmFtZXMKCiAgTWVubyA9IGMoIkthdGFyw61uYSIsICJKw6FuIiwgIkFsZXhhbmRlciIpCiAgVmVrID0gYygyNCwgMjAsIDIxKQogIEJvZHkgPSBjKDg3LCA5NiwgNzUpCmBgYAoKVGlldG8gdHJpIHByZW1lbm7DqSBuaWUgc8O6IHphdGlhxL4gbmlqYWtvIHByZXBvamVuw6ksIHByZWRzdGF2dWrDuiBpem9sb3ZhbsOpIHN0xLpwY2UgdGFidcS+a3kuIERvIHRhYnXEvmt5IGljaCBzcG9qw61tZSBuYXNsZWRvdm5lCgpgYGB7cn0KdWRhamUgPC0gZGF0YS5mcmFtZShNZW5vLFZlayxCb2R5KQpwcmludCh1ZGFqZSkKYGBgCgoKVnlzdmV0bGVuaWU6IERhdGFGcmFtZSBtw6EgdHJpIHN0xLpwY2U6IE1lbm8sIFZlayBhIEJvZHkuIE5pZWt0b3LDqSBvcGVyw6FjaWUgcyDDumRham1pIG9yZ2FuaXpvdmFuw71taSB2IC5kYXRhLmZyYW1lLiBzw7ogdXZlZGVuw6kgbmFzbGVkb3ZuZQoKYGBge3J9CnByaW50KHVkYWplJFZlaykgICAgICAgICAgICAgICAgIApwcmludChtZWFuKHVkYWplJFZlaykpICAgICAgICAgICAKcHJpbnQodWRhamVbTWVubz09IkpvemVmIixdKSAgICAgCnByaW50KHVkYWplWzMsXSkgICAgICAgICAgICAgICAgIApwcmludCh1ZGFqZVssMjozXSkgICAgICAgICAgICAgICAKcHJpbnQodWRhamVbMSwxXSkgICAgICAgICAgICAgICAgCnN1bW1hcnkodWRhamUpICAgICAgICAgICAgICAgICAgIApgYGAKCkFrIGNoY2VtZSBwcmlkYcWlIGsgdGFidcS+a2UgZG9kYXRvxI1uw70gc3TEunBlYywgcG90b20gdG8gcm9iw61tZSBuYXNsZWRvdm5lCgpgYGB7cn0KTWFBdXRvIDwtIGMoVFJVRSxGQUxTRSxUUlVFKQp1ZGFqZSA8LSBjYmluZCh1ZGFqZSxNYUF1dG8pCnByaW50KHVkYWplKQpgYGAKCkFrIGNoY2VtZSBwcmlkYcWlIHJpYWRvaywgcG90b20KCmBgYHtyfQojIE5ldyByZWNvcmQgKG11c3QgbWF0Y2ggY29sdW1uIG9yZGVyL3R5cGVzKQpub3Z5LnJpYWRvayA8LSBkYXRhLmZyYW1lKE1lbm8gPSAiVmlrdMOzcmlhIiwgVmVrID0gMTYsIEJvZHkgPSA4OSxNYUF1dG8gPSBGQUxTRSkKCiMgQXBwZW5kCnVkYWplIDwtIHJiaW5kKHVkYWplLCBub3Z5LnJpYWRvaykKcHJpbnQodWRhamUpCmBgYAoKIyMjIFRhYnXEvmt5IHYgcHJvc3RyZWTDrSBrYWJsZWV4dHJhCgoKYGBge3J9CmxpYnJhcnkoa25pdHIpCmxpYnJhcnkoa2FibGVFeHRyYSkKa2FibGUoCiAgdWRhamUsCiMgIGZvcm1hdCwKZGlnaXRzID0gMiwKIyAgcm93Lm5hbWVzID0gTkEsCiMgIGNvbC5uYW1lcyA9IE5BLAogIGFsaWduPWMoImwiLCJjIiwibCIsInIiKSwKICBjYXB0aW9uID0gIlRvdG8gamUgdGFidcS+a2EiCiMgIGxhYmVsID0gTlVMTCwKIyAgZm9ybWF0LmFyZ3MgPSBsaXN0KCksCiMgIGVzY2FwZSA9IFRSVUUsCiAjIC4uLgopICU+JQogICAgICBrYWJsZV9zdHlsaW5nKGJvb3RzdHJhcF9vcHRpb25zID0gYygic3RyaXBlZCIsICJob3ZlciIsICJjb25kZW5zZWQiLCAicmVzcG9uc2l2ZSIpLAogICAgZnVsbF93aWR0aCA9IEZBTFNFLAogICAgcG9zaXRpb24gPSAiY2VudGVyIikKCgoKCgpgYGAKCgoKIyMgVGlkeXZlcnNlIC0gbW9kZXJuw6EgcHLDoWNhIHMgw7pkYWptaQoKVGlkeXZlcnNlIGplIHPDumJvciBrbmnFvm7DrWMsIGt0b3LDqSBtYWrDuiB6amVkbm9kdcWhacWlIHByw6FjdSBzIMO6ZGFqbWkuIE1hasO6IGplZG5vdG7DvSBrb211bmlrYcSNbsO9IMWhdGFuZGFyZCwgdnrDoWpvbW5lIHNhIGRvcGzFiHVqw7ouCgpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQojIExvYWQgdGlkeXZlcnNlCmxpYnJhcnkodGlkeXZlcnNlKQpgYGAKCiMjIyAgZHBseXIgLSBwcmUgbWFuaXB1bMOhY2l1IHMgw7pkYWptaQoKLmRwbHlyLiBwb3NreXR1amUgesOha2xhZG7DqSBtb8W+bm9zdGkgbWFuaXB1bMOhY2llIHMgw7pkYWptaSwgYWtvIG5hcHIuOiAKCjEuIGZpbHRlcigpOiB2eWJlcsOhIHJpYWRreSAKCjEuIHNlbGVjdCgpOiB2eWJlcsOhIHN0xLpwY2UgCgoxLiBtdXRhdGUoKTogdnl0dsOhcmEgbm92w6kgc3TEunBjZSB0YWJ1xL5reSAKCjEuIGFycmFuZ2UoKTogdHJpZWRpIHJpYWRreSAKCjEuIHN1bW1hcmlzZSgpOiBzdW1hcml6dWplCgpWIG5hc2xlZG92bmVqIHVrw6HFvmtlIHZ5dcW+aWplbWUgdHp2LiAucGlwZXMuICU+JSBhbGVibyAlPCUgdW1vxb7FiHVqZSBwb3NpZWxhxaUgdsO9c2xlZGt5IHogamVkbmVqIGZ1bmtjaWUgcHJpYW1vIGRvIHZvbGFuaWUgbmFzbGVkb3ZuZWogZnVua2NpZS4gVG8gdW1vxb7FiHVqZSDEvmFoxaFpdSDEjWl0YXRlxL5ub3PFpSBrw7Nkb3YsIGtvbnZlbmNpYSBzYSB1amFsYSBhIG3DoSDFoWlyb2vDqSBwb3XFvml0aWUuCgojIyMjIFbDvWJlciBhIHRyaWVkZW5pZQoKYGBge3J9CiMgdsO9YmVyIGEgbsOhc2xlZG7DqSB0cmllZGVuaWUKdWRhamUgJT4lCiAgZmlsdGVyKEJvZHkgPiA2MikgJT4lICAgICAKICBhcnJhbmdlKGRlc2MoQm9keSkpICU+JSAgICAgCmthYmxlICU+JQogICAga2FibGVfc3R5bGluZygKICAgIGJvb3RzdHJhcF9vcHRpb25zID0gYygic3RyaXBlZCIsICJob3ZlciIsICJjb25kZW5zZWQiLCAicmVzcG9uc2l2ZSIpLAogICAgZnVsbF93aWR0aCA9IEZBTFNFLAogICAgcG9zaXRpb24gPSAiY2VudGVyIgogICkKYGBgCgojIyMjIFpvc2t1cGVuaWUgYSBzdW1hcml6w6FjaWEKCmBgYHtyfQojIFpvc2t1cMOtIGFuZCBzdW1hcml6dWplCnVkYWplICU+JQogIGdyb3VwX2J5KE1hQXV0bykgJT4lICAgICAgIyB6b3NrdXBpIHphem5hbXkgcG9kbGEgcHJlbWVubmVqIE1hQXV0byBhIHZ5cG9jaXRhIHphIGthemR1IHNrdXBpbnUgamVqIHByaWVtZXIgQm9keQogIHN1bW1hcmlzZSggICAgICAgICAgICAgICAgIyBhIHRha3RpZXogc3BvY2l0YSBwb2NldG5vc3RpIG9ib2NoIHNrdXBpbgogICAgUHJpZW0uQm9keSA9IG1lYW4oQm9keSksCiAgICBjb3VudCA9IG4oKQogICkgJT4lCiBrYWJsZSgKICAgIGNhcHRpb24gPSAiUHJpZW1lcm7DqSBCb2R5IHBvZMS+YSBwcmVtZW5uZWogTWFBdXRvIiwKICAgIGNvbC5uYW1lcyA9IGMoIk3DoSBBdXRvIiwgIlByaWVtZXIgQm9keSIsICJQb8SNZXQiKSwKICAgIGFsaWduID0gImMiCiAgKSAlPiUKICBrYWJsZV9zdHlsaW5nKAogICAgYm9vdHN0cmFwX29wdGlvbnMgPSBjKCJzdHJpcGVkIiwgImhvdmVyIiwgImNvbmRlbnNlZCIsICJyZXNwb25zaXZlIiksCiAgICBmdWxsX3dpZHRoID0gRkFMU0UsCiAgICBwb3NpdGlvbiA9ICJjZW50ZXIiCiAgKQpgYGAKCiMjIyMgVnl0dsOhcmFuaWUgbm92ZWogcHJlbWVubmVqCgpgYGB7cn0KIyBWeXR2w6FyYW5pZSBub3ZlaiBwcmVtZW5uZWoKdWRhamUgJT4lCiAgbXV0YXRlKAogICAgZ3JhZGUgPSBjYXNlX3doZW4oICAgICAjIHZ5dHZhcmEgbm92dSBwcmVtZW5udSBncmFkZSBwb2RsYSBuYXNsZWRvdm5laiByZWxhY25laiBzY2hlbXkKICAgICAgQm9keSA+PSA5MCB+ICJBIiwKICAgICAgQm9keSA+PSA4MCB+ICJCIiwKICAgICAgQm9keSA+PSA3MCB+ICJDIiwKICAgICAgVFJVRSB+ICJEIgogICAgKSwKICAgIFZla1BvUGxub2xldG9zdGkgPSByb3VuZChWZWstMTgsMCkKICApICU+JSAKICBrYWJsZSAlPiUKICAga2FibGVfc3R5bGluZygKICAgIGJvb3RzdHJhcF9vcHRpb25zID0gYygic3RyaXBlZCIsICJob3ZlciIsICJjb25kZW5zZWQiLCAicmVzcG9uc2l2ZSIpLAogICAgZnVsbF93aWR0aCA9IEZBTFNFLAogICAgcG9zaXRpb24gPSAiY2VudGVyIgogICkgCmBgYAoKCiMjIEltcG9ydCDDumRham92IHogb3R2LiBkYXRhYsOhegoKCjEuICpNZW5kZWxleSBEYXRhKiBUdXRvIHNhIGRvc3RhbmVtZSB6IFtNZW5kZWxleSBEYXRhXShodHRwczovL2RhdGEubWVuZGVsZXkuY29tLyl7dGFyZ2V0PSIuYmxhbmsiIHJlbD0ibm9vcGVuZXIifSwga2RlIHNpIMO6ZGFqZSB2aWV0ZSB2b8S+bmUgc3RpYWhuw7rFpS4gw5pkYWplIHNhIHZ6xaVhaHVqw7ogayB1xb4gcHVibGlrb3ZhbsO9bSDEjWzDoW5rb20gdm8gdnlkYXZhdGXEvnN0dmUgRWxzZXZpZXIuIFbDvWJlciBzYSBkw6EgdXJvYmnFpSBqZWRub2R1Y2hvIHphZGFuw61tIGvEvsO6xI1vdsO9Y2ggc2xvdi4KMi4gKkthZ2dsZSBEYXRhKiAgIFR1dG8gc2EgZG9zdGFuZW1lIHogW0thZ2dsZSBEYXRhc2V0c10oaHR0cHM6Ly93d3cua2FnZ2xlLmNvbS9kYXRhc2V0cyl7dGFyZ2V0PSIuYmxhbmsiIHJlbD0ibm9vcGVuZXIifSwga2RlIHNpIMO6ZGFqZSB2aWV0ZSB2b8S+bmUgc3RpYWhuw7rFpS4gw5pkYWplIHNhIHZ6xaVhaHVqw7ogayBwcm9qZWt0b20gcG9kcG9yb3ZhbsO9bSBLYWdnbGUuIFbDvWJlciBzYSBkw6EgdXJvYmnFpSBqZWRub2R1Y2hvIHphZGFuw61tIGvEvsO6xI1vdsO9Y2ggc2xvdi4KMy4gRGF0YWLDoXp5IGtuacW+bsOtYyBSIC0gLmxpYnJhcnkoZGF0YXNldHMpLiBhbGVibyAubGlicmFyeSh3b29sZHJpZGdlKS4gYWxlIGFqIGluw6kgLSBzdGHEjcOtIHNpIGRhxaUgcHLDrWtheiBkYXRhKCkKCmBgYHtyfQpsaWJyYXJ5KGRhdGFzZXRzKQojIGRhdGFzZXRzIGF2YWlsYWJsZSBpbiB0aGUgJ2RhdGFzZXRzJyBwYWNrYWdlIC0gbmFzbGVkb3ZuZSBrb2R5IHphIG1uYSB1cm9iaWwgQ2hhdCBHUFQKZHMgPC0gYXMuZGF0YS5mcmFtZSh1dGlsczo6ZGF0YShwYWNrYWdlID0gImRhdGFzZXRzIikkcmVzdWx0cylbLCBjKCJJdGVtIiwiVGl0bGUiKV0Ka25pdHI6OmthYmxlKGhlYWQoZHMsIDIwKSwgY29sLm5hbWVzID0gYygiRGF0YXNldCIsICJUaXRsZSIpKSAgICMgcHJ2eWNoIDIwIGRhdGFiYXoKIyBrbml6bmljYSBkYXRhc2V0cyBvYnNhaHVqZSBkYXRhYmF6dSBuYXp2YW51IENPMi4gTW96ZW1lIHNhIG5hIG51IG9kdm9sYXZhdCBuYXNsZWRvdm5lLCBha28gbmFwci4gCmhlYWQoQ08yKQpgYGAKCk3DtMW+ZW1lIHBvdcW+acWlIGFqIGRhdGFiw6F6dSB1csSNZW7DuiBwcmUgZWtvYm9tZXRyaXUgLSBwYWNrYWdlIFdvb2xkcmlkZ2UKCmBgYHtyfQojIGluc3RhbGwucGFja2FnZXMoIndvb2xkcmlkZ2UiKQpsaWJyYXJ5KHdvb2xkcmlkZ2UpCmRzIDwtIGFzLmRhdGEuZnJhbWUodXRpbHM6OmRhdGEocGFja2FnZSA9ICJ3b29sZHJpZGdlIikkcmVzdWx0cylbLCBjKCJJdGVtIiwiVGl0bGUiKV0Ka25pdHI6OmthYmxlKGhlYWQoZHMsIDIwKSwgY29sLm5hbWVzID0gYygiRGF0YXNldCIsICJUaXRsZSIpKSAlPiUKICAgIGthYmxlX3N0eWxpbmcoCiAgICBib290c3RyYXBfb3B0aW9ucyA9IGMoInN0cmlwZWQiLCAiaG92ZXIiLCAiY29uZGVuc2VkIiwgInJlc3BvbnNpdmUiKSwKICAgIGZ1bGxfd2lkdGggPSBGQUxTRSwKICAgIHBvc2l0aW9uID0gImNlbnRlciIKICApCmBgYAoKIyMjIEltcG9ydCDDumRham92IHogLmNzdiBhbGVibyAueGxzCgpKYSBzb20gc2kgenZvbGlsIMO6ZGFqZSB6IFtBYm9zZWRlIFRpYW1peXU6IEVudmlyb25tZW50YWwsIFNvY2lhbCwgYW5kIEdvdmVybmFuY2UgUmVwb3J0aW5nIEV2aWRlbmNpbmcgRmlybSBQZXJmb3JtYW5jZSBpbiBFbWVyZ2luZyBFY29ub215XXtodHRwczovL2RhdGEubWVuZGVsZXkuY29tL2RhdGFzZXRzLzdrOHBqaHNyd2IvMX0uIE5hIHN0csOhbmtlIHNhIG5hY2jDoWR6YSBzw7pib3IgLkRhdGFzZXQgRVNHIGFuZCBGaXJtIFBlcmZvcm1hbmNlLnhsc3guLCBrdG9yw70gc29tIHNpIHN0aWFob2wgYSBleHBvcnRvdmFsIGRvIGZvcm3DoXR1IGNzdi4gQWtvIG9kZGXEvm92YcSNIHBvbG/FvmllayBzb20gc2kgenZvbGlsIGJvZGtvxI1pYXJrdSAoc2VtaWNvbG9uIDspLCB2ecW+w612YW0gZGVzYXRpbm7DuiBib2RrdSBhIG5pZSDEjWlhcmt1IGEgdGllxb4gdGV4dG92w6kgcHJlbWVubsOpIHV2w6FkemFtIGFwb3N0cm9mbWkgIi4gViBwcnZvbSByaWFka3Ugc2EgbmFjaMOhZHphasO6IG7DoXp2eSBzdMS6cGNvdiwga3RvcsOpIG5lc2vDtHIgYnVkw7ogdnlzdHVwb3ZhxaUgYWtvIHByZW1lbm7DqS4gVGllIG9ic2FodWrDuiBtZWR6ZXJ5LCDEjW8gamUgdiB6w6F6dmUgcHJlbWVubmVqIG5lcHLDrXB1c3Ruw6kgYSBuYWhyYWRpbCBzb20gaWNoIHBvZHRyaG92w6F0a29tICIuIi4gIAoKIVtOw6FoxL5hZCBuYSB4bHMgZGF0YWLDoXp1IG90dm9yZW7DuiB2IHRhYnXEvmtvdm9tIHByb2Nlc29yZV0ob2JyYXpreS91ZGFqZXhscy5qcGcpe3dpZHRoPTEwMCV9CgohW07DoWjEvmFkIG5hIGNzdiBkYXRhYsOhenUgb3R2b3JlbsO6IHYgdGV4dG92b20gcHJvY2Vzb3JlXShvYnJhemt5L3VkYWplY3N2LmpwZyl7d2lkdGg9MTAwJX0KClBvdG9tIHXFviBzdGHEjcOtIGltcG9ydG92YcWlIMO6ZGFqZSBkbyAuZGF0YS5mcmFtZS4sIGEgdG8gbmFzbGVkb3ZuZQoKYGBge3J9CnVkYWplIDwtIHJlYWQuY3N2MigidWRhamUvRGF0YXNldCBFU0cgYW5kIEZpcm0gUGVyZm9ybWFuY2UuY3N2IixoZWFkZXI9VFJVRSxzZXA9IjsiLGRlYz0iLiIpCmhlYWQodWRhamUpICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBuaWVrb2xrbyBwcnZ5Y2ggcmlhZGtvdgpjb2xuYW1lcyh1ZGFqZSkgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgbmF6dnkgcHJlbWVubnljaApgYGAKCiMjIEdyYWZ5CgoKIyMjIGdncGxvdDIgLSBrbmnFvm5pY2EgcHJlIGdyYWZ5CgpWw71iZXIgYSBuw6FzbGVkbsOpIHRyaWVkZW5pZQpgYGB7cn0KbGlicmFyeShkcGx5cikKCnVkYWplLjIwMTMgPC0gdWRhamUgJT4lCiAgZmlsdGVyKFlFQVJTID09IDIwMTMpICU+JQogIHNlbGVjdChSRVRVUk4uT04uQVNTRVRTLCBFU0cuSU5ERVgsIERFQlQuVE8uQVNTRVQsIEZJUk0uU0laRSkKYGBgCgpLbmnFvm5pY2EgLmdncGxvdDIuIGplIHYgc8O6xI1hc25vc3RpIG5hasSNYXN0ZWrFoWllIHBvdcW+w612YW7DoSBncmFmaWNrw6Ega25pxb5uaWNhLCBwcmnEjW9tIHByZWRwcmlwcmF2ZW7DqSBrw7NkeSBrIGplZG5vdGxpdsO9bSBvYnLDoXprb20gc2kgdmlldGUgbsOhanPFpSB2IFtSIEdyYXBoIEdhbGxlcnldKGh0dHBzOi8vci1ncmFwaC1nYWxsZXJ5LmNvbS8pLiBUdSBzaSB1dmVkaWVtZSBqZWRub2R1Y2jFoWllIHogbmljaC4KCiMjIyMgU2NhdHRlciBwbG90CgpgYGB7cn0KIyBCYXNpYyBzY2F0dGVyIHBsb3QKbGlicmFyeShnZ3Bsb3QyKQpnZ3Bsb3QodWRhamUuMjAxMywgYWVzKHggPSBGSVJNLlNJWkUsIHkgPSBFU0cuSU5ERVgpKSArICAgICAgICAgICAgIyBzcGVjaWZpa2FjaWEgb3NpCiAgZ2VvbV9wb2ludCgpICsgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIHR5cCBncmFmdSAtIHNjYXR0ZXJwbG90CiAgdGhlbWVfbWluaW1hbCgpICsKICBsYWJzKHRpdGxlID0gIkVTRyBpbmRleCIsIHggPSAiVmXEvmtvc8WlIGZpcm15IiwgeSA9ICJTY29yZSIpICAgICAgIyBvem5hY2VuaWUgb3NpCmBgYAoKIyMjIyBCb3hwbG90CgpgYGB7cn0KIyBCYXIgcGxvdCB3aXRoIGdyb3VwaW5nCmxpYnJhcnkoZ2dwbG90MikKCmxpYnJhcnkoZ2dwbG90MikKCmdncGxvdCh1ZGFqZSwgYWVzKHggPSBmYWN0b3IoWUVBUlMpLCB5ID0gRVNHLklOREVYKSkgKyAgICAgICAgIyBzcGVjaWZpa2FjaWEgb3NpCiAgZ2VvbV9ib3hwbG90KGZpbGwgPSAibGlnaHRibHVlIiwgY29sb3IgPSAiZGFya2JsdWUiKSArICAgICAgIyB0eXAgZ3JhZnUgLSBib3hwbG90CiAgbGFicyggICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBvem5hY2VuaWUgb3NpLCBuYXpvdiBncmFmdQogICAgdGl0bGUgPSAiRVNHIEluZGV4IGJ5IFllYXJzIiwKICAgIHggPSAiWWVhciIsCiAgICB5ID0gIkVTRyBJbmRleCIKICApICsKICB0aGVtZV9taW5pbWFsKCkKYGBgCgojIFrDoWtsYWRuw6kgxaF0YXRpc3Rpa3kuIAoKCiMjIGtuaXRyIC0gdGFidcS+a2EKCmBgYHtyfQpsaWJyYXJ5KGRwbHlyKQpsaWJyYXJ5KGtuaXRyKQoKIyBTdW1tYXJpc2UgYmFzaWMgc3RhdGlzdGljcwplc2cuc3RhdHMgPC0gdWRhamUgJT4lCiAgZmlsdGVyKFlFQVJTICVpbiUgMjAxMzoyMDE2KSAlPiUKICBncm91cF9ieShZRUFSUykgJT4lCiAgc3VtbWFyaXNlKAogICAgbiAgICAgPSBuKCksCiAgICBtZWFuICA9IG1lYW4oRVNHLklOREVYLCBuYS5ybSA9IFRSVUUpLAogICAgc2QgICAgPSBzZChFU0cuSU5ERVgsIG5hLnJtID0gVFJVRSksCiAgICBtaW4gICA9IG1pbihFU0cuSU5ERVgsIG5hLnJtID0gVFJVRSksCiAgICBxMjUgICA9IHF1YW50aWxlKEVTRy5JTkRFWCwgMC4yNSwgbmEucm0gPSBUUlVFKSwKICAgIG1lZGlhbj0gbWVkaWFuKEVTRy5JTkRFWCwgbmEucm0gPSBUUlVFKSwKICAgIHE3NSAgID0gcXVhbnRpbGUoRVNHLklOREVYLCAwLjc1LCBuYS5ybSA9IFRSVUUpLAogICAgbWF4ICAgPSBtYXgoRVNHLklOREVYLCBuYS5ybSA9IFRSVUUpLAogICAgLmdyb3VwcyA9ICJkcm9wIgogICkKCiMgQ3JlYXRlIGtuaXRyIHRhYmxlCmthYmxlKGVzZy5zdGF0cywgZGlnaXRzID0gMiwgY2FwdGlvbiA9ICJCYXNpYyBzdGF0aXN0aWNzIG9mIEVTRyBJbmRleCAoMjAxM+KAkzIwMTYpIikKYGBgCgphbGVibyBrcmFqxaFpZSB0YWJ1xL5reSBzIHBvbW9jb3UgLmthYmxlRXh0cmEuOgoKYGBge3J9CmxpYnJhcnkoZHBseXIpCmxpYnJhcnkoa25pdHIpCmxpYnJhcnkoa2FibGVFeHRyYSkKCiMgU3VtbWFyaXNlIGJhc2ljIHN0YXRpc3RpY3MKZXNnLnN0YXRzIDwtIHVkYWplICU+JQogIGZpbHRlcihZRUFSUyAlaW4lIDIwMTM6MjAxNikgJT4lCiAgZ3JvdXBfYnkoWUVBUlMpICU+JQogIHN1bW1hcmlzZSgKICAgIG4gICAgICA9IG4oKSwKICAgIG1lYW4gICA9IG1lYW4oRVNHLklOREVYLCBuYS5ybSA9IFRSVUUpLAogICAgc2QgICAgID0gc2QoRVNHLklOREVYLCBuYS5ybSA9IFRSVUUpLAogICAgbWluICAgID0gbWluKEVTRy5JTkRFWCwgbmEucm0gPSBUUlVFKSwKICAgIHEyNSAgICA9IHF1YW50aWxlKEVTRy5JTkRFWCwgMC4yNSwgbmEucm0gPSBUUlVFKSwKICAgIG1lZGlhbiA9IG1lZGlhbihFU0cuSU5ERVgsIG5hLnJtID0gVFJVRSksCiAgICBxNzUgICAgPSBxdWFudGlsZShFU0cuSU5ERVgsIDAuNzUsIG5hLnJtID0gVFJVRSksCiAgICBtYXggICAgPSBtYXgoRVNHLklOREVYLCBuYS5ybSA9IFRSVUUpLAogICAgLmdyb3VwcyA9ICJkcm9wIgogICkKCiMgQ3JlYXRlIHN0eWxlZCBrYWJsZUV4dHJhIHRhYmxlCmVzZy5zdGF0cyAlPiUKICBrYWJsZShkaWdpdHMgPSAyLCBjYXB0aW9uID0gIkJhc2ljIHN0YXRpc3RpY3Mgb2YgRVNHIEluZGV4ICgyMDEz4oCTMjAxNikiKSAlPiUKICBrYWJsZV9zdHlsaW5nKGZ1bGxfd2lkdGggPSBGQUxTRSwgYm9vdHN0cmFwX29wdGlvbnMgPSBjKCJzdHJpcGVkIiwgImhvdmVyIiwgImNvbmRlbnNlZCIpKSAlPiUKICBjb2x1bW5fc3BlYygxLCBib2xkID0gVFJVRSkgJT4lICAgICAgICAgICMgbWFrZSB5ZWFycyBib2xkCiAgcm93X3NwZWMoMCwgYm9sZCA9IFRSVUUsIGJhY2tncm91bmQgPSAiI2YyZjJmMiIpICU+JSAgIyBzdHlsZSBoZWFkZXIgcm93CiAgYWRkX2hlYWRlcl9hYm92ZShjKCIgIiA9IDIsICJFU0cgSW5kZXggU3RhdGlzdGljcyIgPSA3KSkKYGBgCgoKCiMjIyMgdC10ZXN0OiBQb3Jvdm5hbmllIHByaWVtZXJ1IEVTRyBpbmRleHUgdiByb2tvY2ggMjAxMyBhIDIwMTUKCmBgYHtyfQp0LnRlc3QucmVzdWx0IDwtIHQudGVzdCgKICB1ZGFqZSRFU0cuSU5ERVhbdWRhamUkWUVBUlMgPT0gMjAxM10sCiAgdWRhamUkRVNHLklOREVYW3VkYWplJFlFQVJTID09IDIwMTVdCikKCnByaW50KHQudGVzdC5yZXN1bHQpCmBgYAoKCiMjIyMgQU5PVkE6IENvbXBhcmluZyBSZWFkaW5nIFNjb3JlcyBBY3Jvc3MgUHJvZ3JhbXMKCmBgYHtyfQphbm92YS5yZXN1bHQgPC0gYW92KEVTRy5JTkRFWCB+IFlFQVJTLCBkYXRhID0gdWRhamUpCnN1bW1hcnkoYW5vdmEucmVzdWx0KQpgYGAKCiMjIyMgTGluZWFyIFJlZ3Jlc3Npb246IFByZWRpY3RpbmcgTWF0aCBTY29yZXMKCmBgYHtyfQptb2RlbCA8LSBsbShFU0cuSU5ERVggfiBSRVRVUk4uT04uQVNTRVRTICsgRklSTS5TSVpFICsgREVCVC5UTy5BU1NFVCwgZGF0YSA9IHVkYWplLjIwMTMpCnN1bW1hcnkobW9kZWwpCmBgYAoKCgpgYGB7cn0KIyBpbnN0YWxsLnBhY2thZ2VzKGMoImJyb29tIiwgImthYmxlRXh0cmEiLCAiZHBseXIiLCAic3RyaW5nciIpKQpsaWJyYXJ5KGJyb29tKQpsaWJyYXJ5KGRwbHlyKQpsaWJyYXJ5KGthYmxlRXh0cmEpCmxpYnJhcnkoc3RyaW5ncikKCiMgWW91ciBtb2RlbCAoYWxyZWFkeSBmaXR0ZWQpCiMgbW9kZWwgPC0gbG0oRVNHLklOREVYIH4gUkVUVVJOLk9OLkFTU0VUUyArIEZJUk0uU0laRSArIERFQlQuVE8uQVNTRVQsIGRhdGEgPSB1ZGFqZS4yMDEzKQoKY29lZi50YmwgPC0gdGlkeShtb2RlbCwgY29uZi5pbnQgPSBUUlVFKSAlPiUKICBtdXRhdGUoCiAgICB0ZXJtID0gcmVjb2RlKHRlcm0sCiAgICAgICIoSW50ZXJjZXB0KSIgPSAiSW50ZXJjZXB0IiwKICAgICAgIlJFVFVSTi5PTi5BU1NFVFMiID0gIlJldHVybiBvbiBBc3NldHMiLAogICAgICAiRklSTS5TSVpFIiA9ICJGaXJtIFNpemUiLAogICAgICAiREVCVC5UTy5BU1NFVCIgPSAiRGVidCB0byBBc3NldCIKICAgICksCiAgICBzdGFycyA9IGNhc2Vfd2hlbigKICAgICAgcC52YWx1ZSA8IDAuMDAxIH4gIioqKiIsCiAgICAgIHAudmFsdWUgPCAwLjAxICB+ICIqKiIsCiAgICAgIHAudmFsdWUgPCAwLjA1ICB+ICIqIiwKICAgICAgcC52YWx1ZSA8IDAuMSAgIH4gIsK3IiwKICAgICAgVFJVRSAgICAgICAgICAgIH4gIiIKICAgICkKICApICU+JQogIHRyYW5zbXV0ZSgKICAgIFRlcm0gPSB0ZXJtLAogICAgRXN0aW1hdGUgPSBlc3RpbWF0ZSwKICAgIGBTdGQuIEVycm9yYCA9IHN0ZC5lcnJvciwKICAgIGB0IHZhbHVlYCA9IHN0YXRpc3RpYywKICAgIGBwIHZhbHVlYCA9IHAudmFsdWUsCiAgICBgOTUlIENJYCA9IHN0cl9jKCJbIiwgcm91bmQoY29uZi5sb3csIDMpLCAiLCAiLCByb3VuZChjb25mLmhpZ2gsIDMpLCAiXSIpLAogICAgU2lnID0gc3RhcnMKICApCgpjb2VmLnRibCAlPiUKICBrYWJsZSgKICAgIGRpZ2l0cyA9IDMsCiAgICBjYXB0aW9uID0gIk9MUyBSZWdyZXNzaW9uIENvZWZmaWNpZW50cyAoRVNHLklOREVYIH4gUkVUVVJOLk9OLkFTU0VUUyArIEZJUk0uU0laRSArIERFQlQuVE8uQVNTRVQpIgogICkgJT4lCiAga2FibGVfc3R5bGluZyhmdWxsX3dpZHRoID0gRkFMU0UsIGJvb3RzdHJhcF9vcHRpb25zID0gYygic3RyaXBlZCIsICJob3ZlciIsICJjb25kZW5zZWQiKSkgJT4lCiAgY29sdW1uX3NwZWMoMSwgYm9sZCA9IFRSVUUpICU+JQogIHJvd19zcGVjKDAsIGJvbGQgPSBUUlVFLCBiYWNrZ3JvdW5kID0gIiNmMmYyZjIiKSAlPiUKICBmb290bm90ZSgKICAgIGdlbmVyYWwgPSAiU2lnbmlmLiBjb2RlczogKioqIHA8MC4wMDEsICoqIHA8MC4wMSwgKiBwPDAuMDUsIMK3IHA8MC4xLiIsCiAgICB0aHJlZXBhcnR0YWJsZSA9IFRSVUUKICApCmBgYAoKYGBge3J9CmZpdC50YmwgPC0gZ2xhbmNlKG1vZGVsKSAlPiUKICB0cmFuc211dGUoCiAgICBgUi1zcXVhcmVkYCA9IHIuc3F1YXJlZCwKICAgIGBBZGouIFItc3F1YXJlZGAgPSBhZGouci5zcXVhcmVkLAogICAgYEYtc3RhdGlzdGljYCA9IHN0YXRpc3RpYywKICAgIGBGIHAtdmFsdWVgID0gcC52YWx1ZSwKICAgIGBBSUNgID0gQUlDLAogICAgYEJJQ2AgPSBCSUMsCiAgICBgTnVtLiBvYnMuYCA9IG5vYnMKICApCgpmaXQudGJsICU+JQogIGthYmxlKGRpZ2l0cyA9IDMsIGNhcHRpb24gPSAiTW9kZWwgRml0IFN0YXRpc3RpY3MiKSAlPiUKICBrYWJsZV9zdHlsaW5nKGZ1bGxfd2lkdGggPSBGQUxTRSwgYm9vdHN0cmFwX29wdGlvbnMgPSBjKCJjb25kZW5zZWQiKSkKYGBgCgoKCgoKCiMgSW5mbyB6ZHJvamUgcHJlIMSPYWzFoWllIMWhdMO6ZGl1bQoKIyMjIyBSIFByb2plY3QKCi0gICBbUiBQcm9qZWN0IEhvbWVwYWdlXShodHRwczovL3d3dy5yLXByb2plY3Qub3JnLykgLSBCYXNlIFIgZG93bmxvYWRzLAogICAgbmV3cywgYW5kIGxlYXJuaW5nIHJlc291cmNlcwoKIyMjIyBQb3NpdAoKLSAgIFtSIFN0dWRpbyBEZXNrdG9wXShodHRwczovL3Bvc2l0LmNvL2Rvd25sb2FkL3JzdHVkaW8tZGVza3RvcC8pIC0KICAgIEZlYXR1cmUgcmljaCBlbnZpcm9ubWVudCBmb3Igd29ya2luZyB3aXRoIGRhdGEgaW4gUgotICAgW1IgZm9yIERhdGEgU2NpZW5jZV0oaHR0cHM6Ly9yNGRzLmhhZC5jby5uei8pIC0gQ29tcHJlaGVuc2l2ZSBvbmxpbmUKICAgIGJvb2sKLSAgIFtSU3R1ZGlvCiAgICBDaGVhdHNoZWV0c10oaHR0cHM6Ly93d3cucnN0dWRpby5jb20vcmVzb3VyY2VzL2NoZWF0c2hlZXRzLykgLSBRdWljawogICAgcmVmZXJlbmNlIGd1aWRlcwoKIyMjIyBDb21tdW5pdHkgUmVzb3VyY2VzCgotICAgW1ItYmxvZ2dlcnNdKGh0dHBzOi8vd3d3LnItYmxvZ2dlcnMuY29tLykgLSBCbG9nIGFnZ3JlZ2F0b3IgZm9yIFIKICAgIG5ld3MgYW5kIHR1dG9yaWFscwotICAgW1N0YWNrIE92ZXJmbG93IC0gUgogICAgdGFnXShodHRwczovL3N0YWNrb3ZlcmZsb3cuY29tL3F1ZXN0aW9ucy90YWdnZWQvcikgLSBRJkEgY29tbXVuaXR5Ci0gICBbcmRyci5pbyBTbmlwcGV0c10oaHR0cHM6Ly9yZHJyLmlvL3NuaXBwZXRzLykgLSBUZXN0IFIgY29kZSBzbmlwcGV0cwogICAgb25saW5lCi0gICBbQ291cnNlcmEgLSBSCiAgICBQcm9ncmFtbWluZ10oaHR0cHM6Ly93d3cuY291cnNlcmEub3JnL2xlYXJuL3ItcHJvZ3JhbW1pbmcpIC0gT25saW5lCiAgICBjb3Vyc2UKLSAgIFtEYXRhQ2FtcCAtIEludHJvZHVjdGlvbiB0bwogICAgUl0oaHR0cHM6Ly93d3cuZGF0YWNhbXAuY29tL2NvdXJzZXMvZnJlZS1pbnRyb2R1Y3Rpb24tdG8tcikgLQogICAgSW50ZXJhY3RpdmUgbGVhcm5pbmcgcGxhdGZvcm0KCg==