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

Práca s údajmi

Príklad

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

# Working with data frames

  Meno = c("Alexandra", "Veronika", "Beatrix")
  Vek = c(18, 10, 12)
  Body = c(48, 81, 98)

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] 18 10 12
print(mean(udaje$Vek))           
[1] 13.33333
print(udaje[Meno=="Alexandra",])     
print(udaje[3,])                 
print(udaje[,1:2])               
print(udaje[1,1])                
[1] "Alexandra"
summary(udaje)                   
     Meno                Vek             Body      
 Length:3           Min.   :10.00   Min.   :48.00  
 Class :character   1st Qu.:11.00   1st Qu.:64.50  
 Mode  :character   Median :12.00   Median :81.00  
                    Mean   :13.33   Mean   :75.67  
                    3rd Qu.:15.00   3rd Qu.:89.50  
                    Max.   :18.00   Max.   :98.00  

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

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

Ak chceme pridať riadok, potom

# New record (must match column order/types)
novy.riadok <- data.frame(Meno = "Amélia", Vek = 24, Body = 50, MaAuto = TRUE)

# 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 = "Výsledky študentov v teste"
#  label = NULL,
#  format.args = list(),
#  escape = TRUE,
 # ...
) %>%
      kable_styling(bootstrap_options = c("striped", "hover", "condensed", "responsive"),
    full_width = FALSE,
    position = "center")
Výsledky študentov v teste
Meno Vek Body MaAuto
Alexandra 18 48 TRUE
Veronika 10 81 FALSE
Beatrix 12 98 FALSE
Amélia 24 50 TRUE
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 > 70) %>%     
  arrange(desc(Body)) %>%     
kable %>%
    kable_styling(
    bootstrap_options = c("striped", "hover", "condensed", "responsive"),
    full_width = FALSE,
    position = "center"
  )
Meno Vek Body MaAuto
Beatrix 12 98 FALSE
Veronika 10 81 FALSE

Zoskupenie a sumarizácia

# Zoskupí and sumarizuje
udaje %>%
  group_by(MaAuto) %>%      
  summarise(                
    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 89.5 2
TRUE 49.0 2

Vytváranie novej premennej

# Vytváranie novej premennej
udaje %>%
  mutate(
    grade = case_when(     
      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
Alexandra 18 48 TRUE D 0
Veronika 10 81 FALSE B -8
Beatrix 12 98 FALSE A -6
Amélia 24 50 TRUE D 6

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

LS0tCnRpdGxlOiAiUHLDoWNhIHMgZGF0YWLDoXpvdSIKYXV0aG9yOiAiTW9uaWthIFN6xbFjc292w6EgIDxicj4iCmRhdGU6ICJPa3TDs2JlciAyMDI1IgpvdXRwdXQ6IAogIGh0bWxfbm90ZWJvb2s6CiAgICB0b2M6IHRydWUKICAgIHRvY19mbG9hdDogdHJ1ZQogICAgdGhlbWU6IGpvdXJuYWwKICAgIGhpZ2hsaWdodDogdGFuZ28KZWRpdG9yX29wdGlvbnM6IAogIG1hcmtkb3duOiAKICAgIHdyYXA6IDcyCi0tLQoKYGBge3J9CmtuaXRyOjpvcHRzX2NodW5rJHNldCgKICAgIGVjaG8gPSBUUlVFLAogICAgbWVzc2FnZSA9IEZBTFNFLAogICAgd2FybmluZyA9IEZBTFNFCikKYGBgCgoKCiMgUHLDoWNhIHMgw7pkYWptaQoKIyMjIFByw61rbGFkCgpNYWptZSDDumRhamUgbyDFvmlha29jaCwga3RvcsOpIHByZWRzdGF2dWrDuiB0cmkgcHJlbWVubsOpIC0gTWVubywgVmVrIGEgQm9keToKCmBgYHtyfQojIFdvcmtpbmcgd2l0aCBkYXRhIGZyYW1lcwoKICBNZW5vID0gYygiQWxleGFuZHJhIiwgIlZlcm9uaWthIiwgIkJlYXRyaXgiKQogIFZlayA9IGMoMTgsIDEwLCAxMikKICBCb2R5ID0gYyg0OCwgODEsIDk4KQpgYGAKClRpZXRvIHRyaSBwcmVtZW5uw6kgbmllIHPDuiB6YXRpYcS+IG5pamFrbyBwcmVwb2plbsOpLCBwcmVkc3RhdnVqw7ogaXpvbG92YW7DqSBzdMS6cGNlIHRhYnXEvmt5LiBEbyB0YWJ1xL5reSBpY2ggc3BvasOtbWUgbmFzbGVkb3ZuZQoKYGBge3J9CnVkYWplIDwtIGRhdGEuZnJhbWUoTWVubyxWZWssQm9keSkKcHJpbnQodWRhamUpCmBgYAoKClZ5c3ZldGxlbmllOiBEYXRhRnJhbWUgbcOhIHRyaSBzdMS6cGNlOiBNZW5vLCBWZWsgYSBCb2R5LiBOaWVrdG9yw6kgb3BlcsOhY2llIHMgw7pkYWptaSBvcmdhbml6b3ZhbsO9bWkgdiAuZGF0YS5mcmFtZS4gc8O6IHV2ZWRlbsOpIG5hc2xlZG92bmUKCmBgYHtyfQpwcmludCh1ZGFqZSRWZWspICAgICAgICAgICAgICAgICAKcHJpbnQobWVhbih1ZGFqZSRWZWspKSAgICAgICAgICAgCnByaW50KHVkYWplW01lbm89PSJBbGV4YW5kcmEiLF0pICAgICAKcHJpbnQodWRhamVbMyxdKSAgICAgICAgICAgICAgICAgCnByaW50KHVkYWplWywxOjJdKSAgICAgICAgICAgICAgIApwcmludCh1ZGFqZVsxLDFdKSAgICAgICAgICAgICAgICAKc3VtbWFyeSh1ZGFqZSkgICAgICAgICAgICAgICAgICAgCmBgYAoKQWsgY2hjZW1lIHByaWRhxaUgayB0YWJ1xL5rZSBkb2RhdG/EjW7DvSBzdMS6cGVjLCBwb3RvbSB0byByb2LDrW1lIG5hc2xlZG92bmUKCmBgYHtyfQpNYUF1dG8gPC0gYyhUUlVFLEZBTFNFLEZBTFNFKQp1ZGFqZSA8LSBjYmluZCh1ZGFqZSxNYUF1dG8pCnByaW50KHVkYWplKQpgYGAKCkFrIGNoY2VtZSBwcmlkYcWlIHJpYWRvaywgcG90b20KCmBgYHtyfQojIE5ldyByZWNvcmQgKG11c3QgbWF0Y2ggY29sdW1uIG9yZGVyL3R5cGVzKQpub3Z5LnJpYWRvayA8LSBkYXRhLmZyYW1lKE1lbm8gPSAiQW3DqWxpYSIsIFZlayA9IDI0LCBCb2R5ID0gNTAsIE1hQXV0byA9IFRSVUUpCgojIEFwcGVuZAp1ZGFqZSA8LSByYmluZCh1ZGFqZSwgbm92eS5yaWFkb2spCnByaW50KHVkYWplKQpgYGAKCiMjIyBUYWJ1xL5reSB2IHByb3N0cmVkw60ga2FibGVleHRyYQoKCmBgYHtyfQpsaWJyYXJ5KGtuaXRyKQpsaWJyYXJ5KGthYmxlRXh0cmEpCmthYmxlKAogIHVkYWplLAojICBmb3JtYXQsCmRpZ2l0cyA9IDIsCiMgIHJvdy5uYW1lcyA9IE5BLAojICBjb2wubmFtZXMgPSBOQSwKICBhbGlnbj1jKCJsIiwgImMiLCAibCIsICJyIiksCiAgY2FwdGlvbiA9ICJWw71zbGVka3kgxaF0dWRlbnRvdiB2IHRlc3RlIgojICBsYWJlbCA9IE5VTEwsCiMgIGZvcm1hdC5hcmdzID0gbGlzdCgpLAojICBlc2NhcGUgPSBUUlVFLAogIyAuLi4KKSAlPiUKICAgICAga2FibGVfc3R5bGluZyhib290c3RyYXBfb3B0aW9ucyA9IGMoInN0cmlwZWQiLCAiaG92ZXIiLCAiY29uZGVuc2VkIiwgInJlc3BvbnNpdmUiKSwKICAgIGZ1bGxfd2lkdGggPSBGQUxTRSwKICAgIHBvc2l0aW9uID0gImNlbnRlciIpCgpgYGAKCiMjIFRpZHl2ZXJzZSAtIG1vZGVybsOhIHByw6FjYSBzIMO6ZGFqbWkKClRpZHl2ZXJzZSBqZSBzw7pib3Iga25pxb5uw61jLCBrdG9yw6kgbWFqw7ogemplZG5vZHXFoWnFpSBwcsOhY3UgcyDDumRham1pLiBNYWrDuiBqZWRub3Ruw70ga29tdW5pa2HEjW7DvSDFoXRhbmRhcmQsIHZ6w6Fqb21uZSBzYSBkb3BsxYh1asO6LgoKYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KIyBMb2FkIHRpZHl2ZXJzZQpsaWJyYXJ5KHRpZHl2ZXJzZSkKYGBgCgojIyMgIGRwbHlyIC0gcHJlIG1hbmlwdWzDoWNpdSBzIMO6ZGFqbWkKCi5kcGx5ci4gcG9za3l0dWplIHrDoWtsYWRuw6kgbW/Fvm5vc3RpIG1hbmlwdWzDoWNpZSBzIMO6ZGFqbWksIGFrbyBuYXByLjogCgoxLiBmaWx0ZXIoKTogdnliZXLDoSByaWFka3kgCgoxLiBzZWxlY3QoKTogdnliZXLDoSBzdMS6cGNlIAoKMS4gbXV0YXRlKCk6IHZ5dHbDoXJhIG5vdsOpIHN0xLpwY2UgdGFidcS+a3kgCgoxLiBhcnJhbmdlKCk6IHRyaWVkaSByaWFka3kgCgoxLiBzdW1tYXJpc2UoKTogc3VtYXJpenVqZQoKViBuYXNsZWRvdm5laiB1a8Ohxb5rZSB2eXXFvmlqZW1lIHR6di4gLnBpcGVzLiAlPiUgYWxlYm8gJTwlIHVtb8W+xYh1amUgcG9zaWVsYcWlIHbDvXNsZWRreSB6IGplZG5laiBmdW5rY2llIHByaWFtbyBkbyB2b2xhbmllIG5hc2xlZG92bmVqIGZ1bmtjaWUuIFRvIHVtb8W+xYh1amUgxL5haMWhaXUgxI1pdGF0ZcS+bm9zxaUga8OzZG92LCBrb252ZW5jaWEgc2EgdWphbGEgYSBtw6EgxaFpcm9rw6kgcG91xb5pdGllLgoKIyMjIyBWw71iZXIgYSB0cmllZGVuaWUKCmBgYHtyfQojIHbDvWJlciBhIG7DoXNsZWRuw6kgdHJpZWRlbmllCnVkYWplICU+JQogIGZpbHRlcihCb2R5ID4gNzApICU+JSAgICAgCiAgYXJyYW5nZShkZXNjKEJvZHkpKSAlPiUgICAgIAprYWJsZSAlPiUKICAgIGthYmxlX3N0eWxpbmcoCiAgICBib290c3RyYXBfb3B0aW9ucyA9IGMoInN0cmlwZWQiLCAiaG92ZXIiLCAiY29uZGVuc2VkIiwgInJlc3BvbnNpdmUiKSwKICAgIGZ1bGxfd2lkdGggPSBGQUxTRSwKICAgIHBvc2l0aW9uID0gImNlbnRlciIKICApCmBgYAoKIyMjIyBab3NrdXBlbmllIGEgc3VtYXJpesOhY2lhCgpgYGB7cn0KIyBab3NrdXDDrSBhbmQgc3VtYXJpenVqZQp1ZGFqZSAlPiUKICBncm91cF9ieShNYUF1dG8pICU+JSAgICAgIAogIHN1bW1hcmlzZSggICAgICAgICAgICAgICAgCiAgICBQcmllbS5Cb2R5ID0gbWVhbihCb2R5KSwKICAgIGNvdW50ID0gbigpCiAgKSAlPiUKIGthYmxlKAogICAgY2FwdGlvbiA9ICJQcmllbWVybsOpIEJvZHkgcG9kxL5hIHByZW1lbm5laiBNYUF1dG8iLAogICAgY29sLm5hbWVzID0gYygiTcOhIEF1dG8iLCAiUHJpZW1lciBCb2R5IiwgIlBvxI1ldCIpLAogICAgYWxpZ24gPSAiYyIKICApICU+JQogIGthYmxlX3N0eWxpbmcoCiAgICBib290c3RyYXBfb3B0aW9ucyA9IGMoInN0cmlwZWQiLCAiaG92ZXIiLCAiY29uZGVuc2VkIiwgInJlc3BvbnNpdmUiKSwKICAgIGZ1bGxfd2lkdGggPSBGQUxTRSwKICAgIHBvc2l0aW9uID0gImNlbnRlciIKICApCmBgYAoKIyMjIyBWeXR2w6FyYW5pZSBub3ZlaiBwcmVtZW5uZWoKCmBgYHtyfQojIFZ5dHbDoXJhbmllIG5vdmVqIHByZW1lbm5lagp1ZGFqZSAlPiUKICBtdXRhdGUoCiAgICBncmFkZSA9IGNhc2Vfd2hlbiggICAgIAogICAgICBCb2R5ID49IDkwIH4gIkEiLAogICAgICBCb2R5ID49IDgwIH4gIkIiLAogICAgICBCb2R5ID49IDcwIH4gIkMiLAogICAgICBUUlVFIH4gIkQiCiAgICApLAogICAgVmVrUG9QbG5vbGV0b3N0aSA9IHJvdW5kKFZlay0xOCwwKQogICkgJT4lIAogIGthYmxlICU+JQogICBrYWJsZV9zdHlsaW5nKAogICAgYm9vdHN0cmFwX29wdGlvbnMgPSBjKCJzdHJpcGVkIiwgImhvdmVyIiwgImNvbmRlbnNlZCIsICJyZXNwb25zaXZlIiksCiAgICBmdWxsX3dpZHRoID0gRkFMU0UsCiAgICBwb3NpdGlvbiA9ICJjZW50ZXIiCiAgKSAKYGBgCgoKIyMgSW1wb3J0IMO6ZGFqb3YgeiBvdHYuIGRhdGFiw6F6CgoKMS4gKk1lbmRlbGV5IERhdGEqIFR1dG8gc2EgZG9zdGFuZW1lIHogW01lbmRlbGV5IERhdGFdKGh0dHBzOi8vZGF0YS5tZW5kZWxleS5jb20vKXt0YXJnZXQ9Ii5ibGFuayIgcmVsPSJub29wZW5lciJ9LCBrZGUgc2kgw7pkYWplIHZpZXRlIHZvxL5uZSBzdGlhaG7DusWlLiDDmmRhamUgc2EgdnrFpWFodWrDuiBrIHXFviBwdWJsaWtvdmFuw71tIMSNbMOhbmtvbSB2byB2eWRhdmF0ZcS+c3R2ZSBFbHNldmllci4gVsO9YmVyIHNhIGTDoSB1cm9iacWlIGplZG5vZHVjaG8gemFkYW7DrW0ga8S+w7rEjW92w71jaCBzbG92LgoyLiAqS2FnZ2xlIERhdGEqICAgVHV0byBzYSBkb3N0YW5lbWUgeiBbS2FnZ2xlIERhdGFzZXRzXShodHRwczovL3d3dy5rYWdnbGUuY29tL2RhdGFzZXRzKXt0YXJnZXQ9Ii5ibGFuayIgcmVsPSJub29wZW5lciJ9LCBrZGUgc2kgw7pkYWplIHZpZXRlIHZvxL5uZSBzdGlhaG7DusWlLiDDmmRhamUgc2EgdnrFpWFodWrDuiBrIHByb2pla3RvbSBwb2Rwb3JvdmFuw71tIEthZ2dsZS4gVsO9YmVyIHNhIGTDoSB1cm9iacWlIGplZG5vZHVjaG8gemFkYW7DrW0ga8S+w7rEjW92w71jaCBzbG92LgozLiBEYXRhYsOhenkga25pxb5uw61jIFIgLSAubGlicmFyeShkYXRhc2V0cykuIGFsZWJvIC5saWJyYXJ5KHdvb2xkcmlkZ2UpLiBhbGUgYWogaW7DqSAtIHN0YcSNw60gc2kgZGHFpSBwcsOta2F6IGRhdGEoKQoKYGBge3J9CmxpYnJhcnkoZGF0YXNldHMpCiMgZGF0YXNldHMgYXZhaWxhYmxlIGluIHRoZSAnZGF0YXNldHMnIHBhY2thZ2UgLSBuYXNsZWRvdm5lIGtvZHkgemEgbW5hIHVyb2JpbCBDaGF0IEdQVApkcyA8LSBhcy5kYXRhLmZyYW1lKHV0aWxzOjpkYXRhKHBhY2thZ2UgPSAiZGF0YXNldHMiKSRyZXN1bHRzKVssIGMoIkl0ZW0iLCJUaXRsZSIpXQprbml0cjo6a2FibGUoaGVhZChkcywgMjApLCBjb2wubmFtZXMgPSBjKCJEYXRhc2V0IiwgIlRpdGxlIikpICAgIyBwcnZ5Y2ggMjAgZGF0YWJhegojIGtuaXpuaWNhIGRhdGFzZXRzIG9ic2FodWplIGRhdGFiYXp1IG5henZhbnUgQ08yLiBNb3plbWUgc2EgbmEgbnUgb2R2b2xhdmF0IG5hc2xlZG92bmUsIGFrbyBuYXByLiAKaGVhZChDTzIpCmBgYAoKTcO0xb5lbWUgcG91xb5pxaUgYWogZGF0YWLDoXp1IHVyxI1lbsO6IHByZSBla29ib21ldHJpdSAtIHBhY2thZ2UgV29vbGRyaWRnZQoKYGBge3J9CiMgaW5zdGFsbC5wYWNrYWdlcygid29vbGRyaWRnZSIpCmxpYnJhcnkod29vbGRyaWRnZSkKZHMgPC0gYXMuZGF0YS5mcmFtZSh1dGlsczo6ZGF0YShwYWNrYWdlID0gIndvb2xkcmlkZ2UiKSRyZXN1bHRzKVssIGMoIkl0ZW0iLCJUaXRsZSIpXQprbml0cjo6a2FibGUoaGVhZChkcywgMjApLCBjb2wubmFtZXMgPSBjKCJEYXRhc2V0IiwgIlRpdGxlIikpICU+JQogICAga2FibGVfc3R5bGluZygKICAgIGJvb3RzdHJhcF9vcHRpb25zID0gYygic3RyaXBlZCIsICJob3ZlciIsICJjb25kZW5zZWQiLCAicmVzcG9uc2l2ZSIpLAogICAgZnVsbF93aWR0aCA9IEZBTFNFLAogICAgcG9zaXRpb24gPSAiY2VudGVyIgogICkKYGBgCgojIyMgSW1wb3J0IMO6ZGFqb3YgeiAuY3N2IGFsZWJvIC54bHMKCkphIHNvbSBzaSB6dm9saWwgw7pkYWplIHogW0Fib3NlZGUgVGlhbWl5dTogRW52aXJvbm1lbnRhbCwgU29jaWFsLCBhbmQgR292ZXJuYW5jZSBSZXBvcnRpbmcgRXZpZGVuY2luZyBGaXJtIFBlcmZvcm1hbmNlIGluIEVtZXJnaW5nIEVjb25vbXlde2h0dHBzOi8vZGF0YS5tZW5kZWxleS5jb20vZGF0YXNldHMvN2s4cGpoc3J3Yi8xfS4gTmEgc3Ryw6Fua2Ugc2EgbmFjaMOhZHphIHPDumJvciAuRGF0YXNldCBFU0cgYW5kIEZpcm0gUGVyZm9ybWFuY2UueGxzeC4sIGt0b3LDvSBzb20gc2kgc3RpYWhvbCBhIGV4cG9ydG92YWwgZG8gZm9ybcOhdHUgY3N2LiBBa28gb2RkZcS+b3ZhxI0gcG9sb8W+aWVrIHNvbSBzaSB6dm9saWwgYm9ka2/EjWlhcmt1IChzZW1pY29sb24gOyksIHZ5xb7DrXZhbSBkZXNhdGlubsO6IGJvZGt1IGEgbmllIMSNaWFya3UgYSB0aWXFviB0ZXh0b3bDqSBwcmVtZW5uw6kgdXbDoWR6YW0gYXBvc3Ryb2ZtaSAiLiBWIHBydm9tIHJpYWRrdSBzYSBuYWNow6FkemFqw7ogbsOhenZ5IHN0xLpwY292LCBrdG9yw6kgbmVza8O0ciBidWTDuiB2eXN0dXBvdmHFpSBha28gcHJlbWVubsOpLiBUaWUgb2JzYWh1asO6IG1lZHplcnksIMSNbyBqZSB2IHrDoXp2ZSBwcmVtZW5uZWogbmVwcsOtcHVzdG7DqSBhIG5haHJhZGlsIHNvbSBpY2ggcG9kdHJob3bDoXRrb20gIi4iLiAgCgohW07DoWjEvmFkIG5hIHhscyBkYXRhYsOhenUgb3R2b3JlbsO6IHYgdGFidcS+a292b20gcHJvY2Vzb3JlXShvYnJhemt5L3VkYWpleGxzLmpwZyl7d2lkdGg9MTAwJX0KCiFbTsOhaMS+YWQgbmEgY3N2IGRhdGFiw6F6dSBvdHZvcmVuw7ogdiB0ZXh0b3ZvbSBwcm9jZXNvcmVdKG9icmF6a3kvdWRhamVjc3YuanBnKXt3aWR0aD0xMDAlfQoKUG90b20gdcW+IHN0YcSNw60gaW1wb3J0b3ZhxaUgw7pkYWplIGRvIC5kYXRhLmZyYW1lLiwgYSB0byBuYXNsZWRvdm5lCgpgYGB7cn0KdWRhamUgPC0gcmVhZC5jc3YyKCJ1ZGFqZS9EYXRhc2V0IEVTRyBhbmQgRmlybSBQZXJmb3JtYW5jZS5jc3YiLGhlYWRlcj1UUlVFLHNlcD0iOyIsZGVjPSIuIikKaGVhZCh1ZGFqZSkgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIG5pZWtvbGtvIHBydnljaCByaWFka292CmNvbG5hbWVzKHVkYWplKSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBuYXp2eSBwcmVtZW5ueWNoCmBgYAoKIyMgR3JhZnkKCgojIyMgZ2dwbG90MiAtIGtuacW+bmljYSBwcmUgZ3JhZnkKClbDvWJlciBhIG7DoXNsZWRuw6kgdHJpZWRlbmllCmBgYHtyfQpsaWJyYXJ5KGRwbHlyKQoKdWRhamUuMjAxMyA8LSB1ZGFqZSAlPiUKICBmaWx0ZXIoWUVBUlMgPT0gMjAxMykgJT4lCiAgc2VsZWN0KFJFVFVSTi5PTi5BU1NFVFMsIEVTRy5JTkRFWCwgREVCVC5UTy5BU1NFVCwgRklSTS5TSVpFKQpgYGAKCktuacW+bmljYSAuZ2dwbG90Mi4gamUgdiBzw7rEjWFzbm9zdGkgbmFqxI1hc3RlasWhaWUgcG91xb7DrXZhbsOhIGdyYWZpY2vDoSBrbmnFvm5pY2EsIHByacSNb20gcHJlZHByaXByYXZlbsOpIGvDs2R5IGsgamVkbm90bGl2w71tIG9icsOhemtvbSBzaSB2aWV0ZSBuw6Fqc8WlIHYgW1IgR3JhcGggR2FsbGVyeV0oaHR0cHM6Ly9yLWdyYXBoLWdhbGxlcnkuY29tLykuIFR1IHNpIHV2ZWRpZW1lIGplZG5vZHVjaMWhaWUgeiBuaWNoLgoKIyMjIyBTY2F0dGVyIHBsb3QKCmBgYHtyfQojIEJhc2ljIHNjYXR0ZXIgcGxvdApsaWJyYXJ5KGdncGxvdDIpCmdncGxvdCh1ZGFqZS4yMDEzLCBhZXMoeCA9IEZJUk0uU0laRSwgeSA9IEVTRy5JTkRFWCkpICsgICAgICAgICAgICAjIHNwZWNpZmlrYWNpYSBvc2kKICBnZW9tX3BvaW50KCkgKyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgdHlwIGdyYWZ1IC0gc2NhdHRlcnBsb3QKICB0aGVtZV9taW5pbWFsKCkgKwogIGxhYnModGl0bGUgPSAiRVNHIGluZGV4IiwgeCA9ICJWZcS+a29zxaUgZmlybXkiLCB5ID0gIlNjb3JlIikgICAgICAjIG96bmFjZW5pZSBvc2kKYGBgCgojIyMjIEJveHBsb3QKCmBgYHtyfQojIEJhciBwbG90IHdpdGggZ3JvdXBpbmcKbGlicmFyeShnZ3Bsb3QyKQoKbGlicmFyeShnZ3Bsb3QyKQoKZ2dwbG90KHVkYWplLCBhZXMoeCA9IGZhY3RvcihZRUFSUyksIHkgPSBFU0cuSU5ERVgpKSArICAgICAgICAjIHNwZWNpZmlrYWNpYSBvc2kKICBnZW9tX2JveHBsb3QoZmlsbCA9ICJsaWdodGJsdWUiLCBjb2xvciA9ICJkYXJrYmx1ZSIpICsgICAgICAjIHR5cCBncmFmdSAtIGJveHBsb3QKICBsYWJzKCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIG96bmFjZW5pZSBvc2ksIG5hem92IGdyYWZ1CiAgICB0aXRsZSA9ICJFU0cgSW5kZXggYnkgWWVhcnMiLAogICAgeCA9ICJZZWFyIiwKICAgIHkgPSAiRVNHIEluZGV4IgogICkgKwogIHRoZW1lX21pbmltYWwoKQpgYGAKCiMgWsOha2xhZG7DqSDFoXRhdGlzdGlreS4gCgoKIyMga25pdHIgLSB0YWJ1xL5rYQoKYGBge3J9CmxpYnJhcnkoZHBseXIpCmxpYnJhcnkoa25pdHIpCgojIFN1bW1hcmlzZSBiYXNpYyBzdGF0aXN0aWNzCmVzZy5zdGF0cyA8LSB1ZGFqZSAlPiUKICBmaWx0ZXIoWUVBUlMgJWluJSAyMDEzOjIwMTYpICU+JQogIGdyb3VwX2J5KFlFQVJTKSAlPiUKICBzdW1tYXJpc2UoCiAgICBuICAgICA9IG4oKSwKICAgIG1lYW4gID0gbWVhbihFU0cuSU5ERVgsIG5hLnJtID0gVFJVRSksCiAgICBzZCAgICA9IHNkKEVTRy5JTkRFWCwgbmEucm0gPSBUUlVFKSwKICAgIG1pbiAgID0gbWluKEVTRy5JTkRFWCwgbmEucm0gPSBUUlVFKSwKICAgIHEyNSAgID0gcXVhbnRpbGUoRVNHLklOREVYLCAwLjI1LCBuYS5ybSA9IFRSVUUpLAogICAgbWVkaWFuPSBtZWRpYW4oRVNHLklOREVYLCBuYS5ybSA9IFRSVUUpLAogICAgcTc1ICAgPSBxdWFudGlsZShFU0cuSU5ERVgsIDAuNzUsIG5hLnJtID0gVFJVRSksCiAgICBtYXggICA9IG1heChFU0cuSU5ERVgsIG5hLnJtID0gVFJVRSksCiAgICAuZ3JvdXBzID0gImRyb3AiCiAgKQoKIyBDcmVhdGUga25pdHIgdGFibGUKa2FibGUoZXNnLnN0YXRzLCBkaWdpdHMgPSAyLCBjYXB0aW9uID0gIkJhc2ljIHN0YXRpc3RpY3Mgb2YgRVNHIEluZGV4ICgyMDEz4oCTMjAxNikiKQpgYGAKCmFsZWJvIGtyYWrFoWllIHRhYnXEvmt5IHMgcG9tb2NvdSAua2FibGVFeHRyYS46CgpgYGB7cn0KbGlicmFyeShkcGx5cikKbGlicmFyeShrbml0cikKbGlicmFyeShrYWJsZUV4dHJhKQoKIyBTdW1tYXJpc2UgYmFzaWMgc3RhdGlzdGljcwplc2cuc3RhdHMgPC0gdWRhamUgJT4lCiAgZmlsdGVyKFlFQVJTICVpbiUgMjAxMzoyMDE2KSAlPiUKICBncm91cF9ieShZRUFSUykgJT4lCiAgc3VtbWFyaXNlKAogICAgbiAgICAgID0gbigpLAogICAgbWVhbiAgID0gbWVhbihFU0cuSU5ERVgsIG5hLnJtID0gVFJVRSksCiAgICBzZCAgICAgPSBzZChFU0cuSU5ERVgsIG5hLnJtID0gVFJVRSksCiAgICBtaW4gICAgPSBtaW4oRVNHLklOREVYLCBuYS5ybSA9IFRSVUUpLAogICAgcTI1ICAgID0gcXVhbnRpbGUoRVNHLklOREVYLCAwLjI1LCBuYS5ybSA9IFRSVUUpLAogICAgbWVkaWFuID0gbWVkaWFuKEVTRy5JTkRFWCwgbmEucm0gPSBUUlVFKSwKICAgIHE3NSAgICA9IHF1YW50aWxlKEVTRy5JTkRFWCwgMC43NSwgbmEucm0gPSBUUlVFKSwKICAgIG1heCAgICA9IG1heChFU0cuSU5ERVgsIG5hLnJtID0gVFJVRSksCiAgICAuZ3JvdXBzID0gImRyb3AiCiAgKQoKIyBDcmVhdGUgc3R5bGVkIGthYmxlRXh0cmEgdGFibGUKZXNnLnN0YXRzICU+JQogIGthYmxlKGRpZ2l0cyA9IDIsIGNhcHRpb24gPSAiQmFzaWMgc3RhdGlzdGljcyBvZiBFU0cgSW5kZXggKDIwMTPigJMyMDE2KSIpICU+JQogIGthYmxlX3N0eWxpbmcoZnVsbF93aWR0aCA9IEZBTFNFLCBib290c3RyYXBfb3B0aW9ucyA9IGMoInN0cmlwZWQiLCAiaG92ZXIiLCAiY29uZGVuc2VkIikpICU+JQogIGNvbHVtbl9zcGVjKDEsIGJvbGQgPSBUUlVFKSAlPiUgICAgICAgICAgIyBtYWtlIHllYXJzIGJvbGQKICByb3dfc3BlYygwLCBib2xkID0gVFJVRSwgYmFja2dyb3VuZCA9ICIjZjJmMmYyIikgJT4lICAjIHN0eWxlIGhlYWRlciByb3cKICBhZGRfaGVhZGVyX2Fib3ZlKGMoIiAiID0gMiwgIkVTRyBJbmRleCBTdGF0aXN0aWNzIiA9IDcpKQpgYGAKCgoKIyMjIyB0LXRlc3Q6IFBvcm92bmFuaWUgcHJpZW1lcnUgRVNHIGluZGV4dSB2IHJva29jaCAyMDEzIGEgMjAxNQoKYGBge3J9CnQudGVzdC5yZXN1bHQgPC0gdC50ZXN0KAogIHVkYWplJEVTRy5JTkRFWFt1ZGFqZSRZRUFSUyA9PSAyMDEzXSwKICB1ZGFqZSRFU0cuSU5ERVhbdWRhamUkWUVBUlMgPT0gMjAxNV0KKQoKcHJpbnQodC50ZXN0LnJlc3VsdCkKYGBgCgoKIyMjIyBBTk9WQTogQ29tcGFyaW5nIFJlYWRpbmcgU2NvcmVzIEFjcm9zcyBQcm9ncmFtcwoKYGBge3J9CmFub3ZhLnJlc3VsdCA8LSBhb3YoRVNHLklOREVYIH4gWUVBUlMsIGRhdGEgPSB1ZGFqZSkKc3VtbWFyeShhbm92YS5yZXN1bHQpCmBgYAoKIyMjIyBMaW5lYXIgUmVncmVzc2lvbjogUHJlZGljdGluZyBNYXRoIFNjb3JlcwoKYGBge3J9Cm1vZGVsIDwtIGxtKEVTRy5JTkRFWCB+IFJFVFVSTi5PTi5BU1NFVFMgKyBGSVJNLlNJWkUgKyBERUJULlRPLkFTU0VULCBkYXRhID0gdWRhamUuMjAxMykKc3VtbWFyeShtb2RlbCkKYGBgCgoKCmBgYHtyfQojIGluc3RhbGwucGFja2FnZXMoYygiYnJvb20iLCAia2FibGVFeHRyYSIsICJkcGx5ciIsICJzdHJpbmdyIikpCmxpYnJhcnkoYnJvb20pCmxpYnJhcnkoZHBseXIpCmxpYnJhcnkoa2FibGVFeHRyYSkKbGlicmFyeShzdHJpbmdyKQoKIyBZb3VyIG1vZGVsIChhbHJlYWR5IGZpdHRlZCkKIyBtb2RlbCA8LSBsbShFU0cuSU5ERVggfiBSRVRVUk4uT04uQVNTRVRTICsgRklSTS5TSVpFICsgREVCVC5UTy5BU1NFVCwgZGF0YSA9IHVkYWplLjIwMTMpCgpjb2VmLnRibCA8LSB0aWR5KG1vZGVsLCBjb25mLmludCA9IFRSVUUpICU+JQogIG11dGF0ZSgKICAgIHRlcm0gPSByZWNvZGUodGVybSwKICAgICAgIihJbnRlcmNlcHQpIiA9ICJJbnRlcmNlcHQiLAogICAgICAiUkVUVVJOLk9OLkFTU0VUUyIgPSAiUmV0dXJuIG9uIEFzc2V0cyIsCiAgICAgICJGSVJNLlNJWkUiID0gIkZpcm0gU2l6ZSIsCiAgICAgICJERUJULlRPLkFTU0VUIiA9ICJEZWJ0IHRvIEFzc2V0IgogICAgKSwKICAgIHN0YXJzID0gY2FzZV93aGVuKAogICAgICBwLnZhbHVlIDwgMC4wMDEgfiAiKioqIiwKICAgICAgcC52YWx1ZSA8IDAuMDEgIH4gIioqIiwKICAgICAgcC52YWx1ZSA8IDAuMDUgIH4gIioiLAogICAgICBwLnZhbHVlIDwgMC4xICAgfiAiwrciLAogICAgICBUUlVFICAgICAgICAgICAgfiAiIgogICAgKQogICkgJT4lCiAgdHJhbnNtdXRlKAogICAgVGVybSA9IHRlcm0sCiAgICBFc3RpbWF0ZSA9IGVzdGltYXRlLAogICAgYFN0ZC4gRXJyb3JgID0gc3RkLmVycm9yLAogICAgYHQgdmFsdWVgID0gc3RhdGlzdGljLAogICAgYHAgdmFsdWVgID0gcC52YWx1ZSwKICAgIGA5NSUgQ0lgID0gc3RyX2MoIlsiLCByb3VuZChjb25mLmxvdywgMyksICIsICIsIHJvdW5kKGNvbmYuaGlnaCwgMyksICJdIiksCiAgICBTaWcgPSBzdGFycwogICkKCmNvZWYudGJsICU+JQogIGthYmxlKAogICAgZGlnaXRzID0gMywKICAgIGNhcHRpb24gPSAiT0xTIFJlZ3Jlc3Npb24gQ29lZmZpY2llbnRzIChFU0cuSU5ERVggfiBSRVRVUk4uT04uQVNTRVRTICsgRklSTS5TSVpFICsgREVCVC5UTy5BU1NFVCkiCiAgKSAlPiUKICBrYWJsZV9zdHlsaW5nKGZ1bGxfd2lkdGggPSBGQUxTRSwgYm9vdHN0cmFwX29wdGlvbnMgPSBjKCJzdHJpcGVkIiwgImhvdmVyIiwgImNvbmRlbnNlZCIpKSAlPiUKICBjb2x1bW5fc3BlYygxLCBib2xkID0gVFJVRSkgJT4lCiAgcm93X3NwZWMoMCwgYm9sZCA9IFRSVUUsIGJhY2tncm91bmQgPSAiI2YyZjJmMiIpICU+JQogIGZvb3Rub3RlKAogICAgZ2VuZXJhbCA9ICJTaWduaWYuIGNvZGVzOiAqKiogcDwwLjAwMSwgKiogcDwwLjAxLCAqIHA8MC4wNSwgwrcgcDwwLjEuIiwKICAgIHRocmVlcGFydHRhYmxlID0gVFJVRQogICkKYGBgCgpgYGB7cn0KZml0LnRibCA8LSBnbGFuY2UobW9kZWwpICU+JQogIHRyYW5zbXV0ZSgKICAgIGBSLXNxdWFyZWRgID0gci5zcXVhcmVkLAogICAgYEFkai4gUi1zcXVhcmVkYCA9IGFkai5yLnNxdWFyZWQsCiAgICBgRi1zdGF0aXN0aWNgID0gc3RhdGlzdGljLAogICAgYEYgcC12YWx1ZWAgPSBwLnZhbHVlLAogICAgYEFJQ2AgPSBBSUMsCiAgICBgQklDYCA9IEJJQywKICAgIGBOdW0uIG9icy5gID0gbm9icwogICkKCmZpdC50YmwgJT4lCiAga2FibGUoZGlnaXRzID0gMywgY2FwdGlvbiA9ICJNb2RlbCBGaXQgU3RhdGlzdGljcyIpICU+JQogIGthYmxlX3N0eWxpbmcoZnVsbF93aWR0aCA9IEZBTFNFLCBib290c3RyYXBfb3B0aW9ucyA9IGMoImNvbmRlbnNlZCIpKQpgYGAKCgoKCgoKIyBJbmZvIHpkcm9qZSBwcmUgxI9hbMWhaWUgxaF0w7pkaXVtCgojIyMjIFIgUHJvamVjdAoKLSAgIFtSIFByb2plY3QgSG9tZXBhZ2VdKGh0dHBzOi8vd3d3LnItcHJvamVjdC5vcmcvKSAtIEJhc2UgUiBkb3dubG9hZHMsCiAgICBuZXdzLCBhbmQgbGVhcm5pbmcgcmVzb3VyY2VzCgojIyMjIFBvc2l0CgotICAgW1IgU3R1ZGlvIERlc2t0b3BdKGh0dHBzOi8vcG9zaXQuY28vZG93bmxvYWQvcnN0dWRpby1kZXNrdG9wLykgLQogICAgRmVhdHVyZSByaWNoIGVudmlyb25tZW50IGZvciB3b3JraW5nIHdpdGggZGF0YSBpbiBSCi0gICBbUiBmb3IgRGF0YSBTY2llbmNlXShodHRwczovL3I0ZHMuaGFkLmNvLm56LykgLSBDb21wcmVoZW5zaXZlIG9ubGluZQogICAgYm9vawotICAgW1JTdHVkaW8KICAgIENoZWF0c2hlZXRzXShodHRwczovL3d3dy5yc3R1ZGlvLmNvbS9yZXNvdXJjZXMvY2hlYXRzaGVldHMvKSAtIFF1aWNrCiAgICByZWZlcmVuY2UgZ3VpZGVzCgojIyMjIENvbW11bml0eSBSZXNvdXJjZXMKCi0gICBbUi1ibG9nZ2Vyc10oaHR0cHM6Ly93d3cuci1ibG9nZ2Vycy5jb20vKSAtIEJsb2cgYWdncmVnYXRvciBmb3IgUgogICAgbmV3cyBhbmQgdHV0b3JpYWxzCi0gICBbU3RhY2sgT3ZlcmZsb3cgLSBSCiAgICB0YWddKGh0dHBzOi8vc3RhY2tvdmVyZmxvdy5jb20vcXVlc3Rpb25zL3RhZ2dlZC9yKSAtIFEmQSBjb21tdW5pdHkKLSAgIFtyZHJyLmlvIFNuaXBwZXRzXShodHRwczovL3JkcnIuaW8vc25pcHBldHMvKSAtIFRlc3QgUiBjb2RlIHNuaXBwZXRzCiAgICBvbmxpbmUKLSAgIFtDb3Vyc2VyYSAtIFIKICAgIFByb2dyYW1taW5nXShodHRwczovL3d3dy5jb3Vyc2VyYS5vcmcvbGVhcm4vci1wcm9ncmFtbWluZykgLSBPbmxpbmUKICAgIGNvdXJzZQotICAgW0RhdGFDYW1wIC0gSW50cm9kdWN0aW9uIHRvCiAgICBSXShodHRwczovL3d3dy5kYXRhY2FtcC5jb20vY291cnNlcy9mcmVlLWludHJvZHVjdGlvbi10by1yKSAtCiAgICBJbnRlcmFjdGl2ZSBsZWFybmluZyBwbGF0Zm9ybQoK