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("Beatrix", "Monika", "Veronika")
  Vek = c(23, 22, 23)
  Body = c(90, 92, 95)

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] 23 22 23
print(mean(udaje$Vek))          
[1] 22.66667
print(udaje[Meno=="Beatrix",])    
print(udaje[3,])                
print(udaje[,2:3])              
print(udaje[2,2])               
[1] 22
summary(udaje)                   
     Meno                Vek             Body      
 Length:3           Min.   :22.00   Min.   :90.00  
 Class :character   1st Qu.:22.50   1st Qu.:91.00  
 Mode  :character   Median :23.00   Median :92.00  
                    Mean   :22.67   Mean   :92.33  
                    3rd Qu.:23.00   3rd Qu.:93.50  
                    Max.   :23.00   Max.   :95.00  

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

novy.riadok <- data.frame(Meno = "Martina", Vek = 25, Body = 85,MaAuto = FALSE)

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
Beatrix 23 90 TRUE
Monika 22 92 FALSE
Veronika 23 95 TRUE
Martina 25 85 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

udaje %>%
  filter(Body > 80) %>%    
  arrange(desc(Body)) %>%    
kable %>%
    kable_styling(
    bootstrap_options = c("striped", "hover", "condensed", "responsive"),
    full_width = FALSE,
    position = "center"
  )
Meno Vek Body MaAuto
Veronika 23 95 TRUE
Monika 22 92 FALSE
Beatrix 23 90 TRUE
Martina 25 85 FALSE

Zoskupenie a sumarizácia

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 88.5 2
TRUE 92.5 2

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
Beatrix 23 90 TRUE A 5
Monika 22 92 FALSE A 4
Veronika 23 95 TRUE A 5
Martina 25 85 FALSE B 7

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

LS0tCnRpdGxlOiAiUHLDoWNhIHMgZGF0YWLDoXpvdSIKYXV0aG9yOiAiQmVhdHJpeCBUw7N0aG92w6EgIDxicj4iCmRhdGU6ICJPa3TDs2JlciAyMDI1IgpvdXRwdXQ6IAogIGh0bWxfbm90ZWJvb2s6CiAgICB0b2M6IHRydWUKICAgIHRvY19mbG9hdDogdHJ1ZQogICAgdGhlbWU6IHVuaXRlZAogICAgaGlnaGxpZ2h0OiB0YW5nbwplZGl0b3Jfb3B0aW9uczogCiAgbWFya2Rvd246IAogICAgd3JhcDogNzIKLS0tCgpgYGB7cn0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KAogICAgZWNobyA9IFRSVUUsCiAgICBtZXNzYWdlID0gRkFMU0UsCiAgICB3YXJuaW5nID0gRkFMU0UKKQpgYGAKCgojIFByw6FjYSBzIMO6ZGFqbWkKCiMjIFRyYWRpxI1uw6EgcHLDoWNhIHMgZGF0YWLDoXpvdQoKUHJlIHByw6FjdSBzIMO6ZGFqbWkgKGRhdGFiw6F6b3UpIHBvdcW+w612YW1lIG5hasSNYXN0ZWrFoWllIGTDoXRvdsO9IHR5cCAuZGF0YS5mcmFtZS4uIEplIHRvIHRhYnXEvmthLCBrdG9yw6EgcG96b3N0w6F2YSB6byBzdMS6cGNvdiByb3psacSNbsO9Y2ggdHlwb3YuIEplZGVuIHJpYWRvayBwcml0b20gcHJlZHN0YXZ1amUgamVkZW4gesOhem5hbSBkYXRhYsOhenkuCgojIyMgUHLDrWtsYWQKCk1ham1lIMO6ZGFqZSBvIMW+aWFrb2NoLCBrdG9yw6kgcHJlZHN0YXZ1asO6IHRyaSBwcmVtZW5uw6kgLSBNZW5vLCBWZWsgYSBCb2R5OgoKYGBge3J9CiMgV29ya2luZyB3aXRoIGRhdGEgZnJhbWVzCgogIE1lbm8gPSBjKCJCZWF0cml4IiwgIk1vbmlrYSIsICJWZXJvbmlrYSIpCiAgVmVrID0gYygyMywgMjIsIDIzKQogIEJvZHkgPSBjKDkwLCA5MiwgOTUpCmBgYAoKVGlldG8gdHJpIHByZW1lbm7DqSBuaWUgc8O6IHphdGlhxL4gbmlqYWtvIHByZXBvamVuw6ksIHByZWRzdGF2dWrDuiBpem9sb3ZhbsOpIHN0xLpwY2UgdGFidcS+a3kuIERvIHRhYnXEvmt5IGljaCBzcG9qw61tZSBuYXNsZWRvdm5lCgpgYGB7cn0KdWRhamUgPC0gZGF0YS5mcmFtZShNZW5vLFZlayxCb2R5KQpwcmludCh1ZGFqZSkKYGBgCgoKVnlzdmV0bGVuaWU6IERhdGFGcmFtZSBtw6EgdHJpIHN0xLpwY2U6IE1lbm8sIFZlayBhIEJvZHkuIE5pZWt0b3LDqSBvcGVyw6FjaWUgcyDDumRham1pIG9yZ2FuaXpvdmFuw71taSB2IC5kYXRhLmZyYW1lLiBzw7ogdXZlZGVuw6kgbmFzbGVkb3ZuZQoKYGBge3J9CnByaW50KHVkYWplJFZlaykgICAgICAgICAgICAgICAgCnByaW50KG1lYW4odWRhamUkVmVrKSkgICAgICAgICAgCnByaW50KHVkYWplW01lbm89PSJCZWF0cml4IixdKSAgICAKcHJpbnQodWRhamVbMyxdKSAgICAgICAgICAgICAgICAKcHJpbnQodWRhamVbLDI6M10pICAgICAgICAgICAgICAKcHJpbnQodWRhamVbMiwyXSkgICAgICAgICAgICAgICAKc3VtbWFyeSh1ZGFqZSkgICAgICAgICAgICAgICAgICAgCmBgYAoKQWsgY2hjZW1lIHByaWRhxaUgayB0YWJ1xL5rZSBkb2RhdG/EjW7DvSBzdMS6cGVjLCBwb3RvbSB0byByb2LDrW1lIG5hc2xlZG92bmUKCmBgYHtyfQpNYUF1dG8gPC0gYyhUUlVFLEZBTFNFLFRSVUUpCnVkYWplIDwtIGNiaW5kKHVkYWplLE1hQXV0bykKcHJpbnQodWRhamUpCmBgYAoKQWsgY2hjZW1lIHByaWRhxaUgcmlhZG9rLCBwb3RvbQoKYGBge3J9Cm5vdnkucmlhZG9rIDwtIGRhdGEuZnJhbWUoTWVubyA9ICJNYXJ0aW5hIiwgVmVrID0gMjUsIEJvZHkgPSA4NSxNYUF1dG8gPSBGQUxTRSkKCnVkYWplIDwtIHJiaW5kKHVkYWplLCBub3Z5LnJpYWRvaykKcHJpbnQodWRhamUpCmBgYAoKIyMjIFRhYnXEvmt5IHYgcHJvc3RyZWTDrSBrYWJsZWV4dHJhCgoKYGBge3J9CmxpYnJhcnkoa25pdHIpCmxpYnJhcnkoa2FibGVFeHRyYSkKa2FibGUoCiAgdWRhamUsCiMgIGZvcm1hdCwKZGlnaXRzID0gMiwKIyAgcm93Lm5hbWVzID0gTkEsCiMgIGNvbC5uYW1lcyA9IE5BLAogIGFsaWduPWMoImwiLCJjIiwibCIsInIiKSwKICBjYXB0aW9uID0gIlRvdG8gamUgdGFidcS+a2EiCiMgIGxhYmVsID0gTlVMTCwKIyAgZm9ybWF0LmFyZ3MgPSBsaXN0KCksCiMgIGVzY2FwZSA9IFRSVUUsCiAjIC4uLgopICU+JQogICAgICBrYWJsZV9zdHlsaW5nKGJvb3RzdHJhcF9vcHRpb25zID0gYygic3RyaXBlZCIsICJob3ZlciIsICJjb25kZW5zZWQiLCAicmVzcG9uc2l2ZSIpLAogICAgZnVsbF93aWR0aCA9IEZBTFNFLAogICAgcG9zaXRpb24gPSAiY2VudGVyIikKCgoKCgpgYGAKCgoKIyMgVGlkeXZlcnNlIC0gbW9kZXJuw6EgcHLDoWNhIHMgw7pkYWptaQoKVGlkeXZlcnNlIGplIHPDumJvciBrbmnFvm7DrWMsIGt0b3LDqSBtYWrDuiB6amVkbm9kdcWhacWlIHByw6FjdSBzIMO6ZGFqbWkuIE1hasO6IGplZG5vdG7DvSBrb211bmlrYcSNbsO9IMWhdGFuZGFyZCwgdnrDoWpvbW5lIHNhIGRvcGzFiHVqw7ouCgpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQojIExvYWQgdGlkeXZlcnNlCmxpYnJhcnkodGlkeXZlcnNlKQpgYGAKCiMjIyAgZHBseXIgLSBwcmUgbWFuaXB1bMOhY2l1IHMgw7pkYWptaQoKLmRwbHlyLiBwb3NreXR1amUgesOha2xhZG7DqSBtb8W+bm9zdGkgbWFuaXB1bMOhY2llIHMgw7pkYWptaSwgYWtvIG5hcHIuOiAKCjEuIGZpbHRlcigpOiB2eWJlcsOhIHJpYWRreSAKCjEuIHNlbGVjdCgpOiB2eWJlcsOhIHN0xLpwY2UgCgoxLiBtdXRhdGUoKTogdnl0dsOhcmEgbm92w6kgc3TEunBjZSB0YWJ1xL5reSAKCjEuIGFycmFuZ2UoKTogdHJpZWRpIHJpYWRreSAKCjEuIHN1bW1hcmlzZSgpOiBzdW1hcml6dWplCgpWIG5hc2xlZG92bmVqIHVrw6HFvmtlIHZ5dcW+aWplbWUgdHp2LiAucGlwZXMuICU+JSBhbGVibyAlPCUgdW1vxb7FiHVqZSBwb3NpZWxhxaUgdsO9c2xlZGt5IHogamVkbmVqIGZ1bmtjaWUgcHJpYW1vIGRvIHZvbGFuaWUgbmFzbGVkb3ZuZWogZnVua2NpZS4gVG8gdW1vxb7FiHVqZSDEvmFoxaFpdSDEjWl0YXRlxL5ub3PFpSBrw7Nkb3YsIGtvbnZlbmNpYSBzYSB1amFsYSBhIG3DoSDFoWlyb2vDqSBwb3XFvml0aWUuCgojIyMjIFbDvWJlciBhIHRyaWVkZW5pZQoKYGBge3J9CnVkYWplICU+JQogIGZpbHRlcihCb2R5ID4gODApICU+JSAgICAKICBhcnJhbmdlKGRlc2MoQm9keSkpICU+JSAgICAKa2FibGUgJT4lCiAgICBrYWJsZV9zdHlsaW5nKAogICAgYm9vdHN0cmFwX29wdGlvbnMgPSBjKCJzdHJpcGVkIiwgImhvdmVyIiwgImNvbmRlbnNlZCIsICJyZXNwb25zaXZlIiksCiAgICBmdWxsX3dpZHRoID0gRkFMU0UsCiAgICBwb3NpdGlvbiA9ICJjZW50ZXIiCiAgKQpgYGAKCiMjIyMgWm9za3VwZW5pZSBhIHN1bWFyaXrDoWNpYQoKYGBge3J9CnVkYWplICU+JQogIGdyb3VwX2J5KE1hQXV0bykgJT4lICAgICAgCiAgc3VtbWFyaXNlKCAgICAgICAgICAgICAgICAKICAgIFByaWVtLkJvZHkgPSBtZWFuKEJvZHkpLAogICAgY291bnQgPSBuKCkKICApICU+JQoga2FibGUoCiAgICBjYXB0aW9uID0gIlByaWVtZXJuw6kgQm9keSBwb2TEvmEgcHJlbWVubmVqIE1hQXV0byIsCiAgICBjb2wubmFtZXMgPSBjKCJNw6EgQXV0byIsICJQcmllbWVyIEJvZHkiLCAiUG/EjWV0IiksCiAgICBhbGlnbiA9ICJjIgogICkgJT4lCiAga2FibGVfc3R5bGluZygKICAgIGJvb3RzdHJhcF9vcHRpb25zID0gYygic3RyaXBlZCIsICJob3ZlciIsICJjb25kZW5zZWQiLCAicmVzcG9uc2l2ZSIpLAogICAgZnVsbF93aWR0aCA9IEZBTFNFLAogICAgcG9zaXRpb24gPSAiY2VudGVyIgogICkKYGBgCgojIyMjIFZ5dHbDoXJhbmllIG5vdmVqIHByZW1lbm5lagoKYGBge3J9CnVkYWplICU+JQogIG11dGF0ZSgKICAgIGdyYWRlID0gY2FzZV93aGVuKCAgICAgCiAgICAgIEJvZHkgPj0gOTAgfiAiQSIsCiAgICAgIEJvZHkgPj0gODAgfiAiQiIsCiAgICAgIEJvZHkgPj0gNzAgfiAiQyIsCiAgICAgIFRSVUUgfiAiRCIKICAgICksCiAgICBWZWtQb1Bsbm9sZXRvc3RpID0gcm91bmQoVmVrLTE4LDApCiAgKSAlPiUgCiAga2FibGUgJT4lCiAgIGthYmxlX3N0eWxpbmcoCiAgICBib290c3RyYXBfb3B0aW9ucyA9IGMoInN0cmlwZWQiLCAiaG92ZXIiLCAiY29uZGVuc2VkIiwgInJlc3BvbnNpdmUiKSwKICAgIGZ1bGxfd2lkdGggPSBGQUxTRSwKICAgIHBvc2l0aW9uID0gImNlbnRlciIKICApIApgYGAKCgojIyBJbXBvcnQgw7pkYWpvdiB6IG90di4gZGF0YWLDoXoKCgoxLiAqTWVuZGVsZXkgRGF0YSogVHV0byBzYSBkb3N0YW5lbWUgeiBbTWVuZGVsZXkgRGF0YV0oaHR0cHM6Ly9kYXRhLm1lbmRlbGV5LmNvbS8pe3RhcmdldD0iLmJsYW5rIiByZWw9Im5vb3BlbmVyIn0sIGtkZSBzaSDDumRhamUgdmlldGUgdm/Evm5lIHN0aWFobsO6xaUuIMOaZGFqZSBzYSB2esWlYWh1asO6IGsgdcW+IHB1Ymxpa292YW7DvW0gxI1sw6Fua29tIHZvIHZ5ZGF2YXRlxL5zdHZlIEVsc2V2aWVyLiBWw71iZXIgc2EgZMOhIHVyb2JpxaUgamVkbm9kdWNobyB6YWRhbsOtbSBrxL7DusSNb3bDvWNoIHNsb3YuCjIuICpLYWdnbGUgRGF0YSogICBUdXRvIHNhIGRvc3RhbmVtZSB6IFtLYWdnbGUgRGF0YXNldHNdKGh0dHBzOi8vd3d3LmthZ2dsZS5jb20vZGF0YXNldHMpe3RhcmdldD0iLmJsYW5rIiByZWw9Im5vb3BlbmVyIn0sIGtkZSBzaSDDumRhamUgdmlldGUgdm/Evm5lIHN0aWFobsO6xaUuIMOaZGFqZSBzYSB2esWlYWh1asO6IGsgcHJvamVrdG9tIHBvZHBvcm92YW7DvW0gS2FnZ2xlLiBWw71iZXIgc2EgZMOhIHVyb2JpxaUgamVkbm9kdWNobyB6YWRhbsOtbSBrxL7DusSNb3bDvWNoIHNsb3YuCjMuIERhdGFiw6F6eSBrbmnFvm7DrWMgUiAtIC5saWJyYXJ5KGRhdGFzZXRzKS4gYWxlYm8gLmxpYnJhcnkod29vbGRyaWRnZSkuIGFsZSBhaiBpbsOpIC0gc3RhxI3DrSBzaSBkYcWlIHByw61rYXogZGF0YSgpCgpgYGB7cn0KbGlicmFyeShkYXRhc2V0cykKIyBkYXRhc2V0cyBhdmFpbGFibGUgaW4gdGhlICdkYXRhc2V0cycgcGFja2FnZSAtIG5hc2xlZG92bmUga29keSB6YSBtbmEgdXJvYmlsIENoYXQgR1BUCmRzIDwtIGFzLmRhdGEuZnJhbWUodXRpbHM6OmRhdGEocGFja2FnZSA9ICJkYXRhc2V0cyIpJHJlc3VsdHMpWywgYygiSXRlbSIsIlRpdGxlIildCmtuaXRyOjprYWJsZShoZWFkKGRzLCAyMCksIGNvbC5uYW1lcyA9IGMoIkRhdGFzZXQiLCAiVGl0bGUiKSkgICAjIHBydnljaCAyMCBkYXRhYmF6CiMga25pem5pY2EgZGF0YXNldHMgb2JzYWh1amUgZGF0YWJhenUgbmF6dmFudSBDTzIuIE1vemVtZSBzYSBuYSBudSBvZHZvbGF2YXQgbmFzbGVkb3ZuZSwgYWtvIG5hcHIuIApoZWFkKENPMikKYGBgCgpNw7TFvmVtZSBwb3XFvmnFpSBhaiBkYXRhYsOhenUgdXLEjWVuw7ogcHJlIGVrb2JvbWV0cml1IC0gcGFja2FnZSBXb29sZHJpZGdlCgpgYGB7cn0KIyBpbnN0YWxsLnBhY2thZ2VzKCJ3b29sZHJpZGdlIikKbGlicmFyeSh3b29sZHJpZGdlKQpkcyA8LSBhcy5kYXRhLmZyYW1lKHV0aWxzOjpkYXRhKHBhY2thZ2UgPSAid29vbGRyaWRnZSIpJHJlc3VsdHMpWywgYygiSXRlbSIsIlRpdGxlIildCmtuaXRyOjprYWJsZShoZWFkKGRzLCAyMCksIGNvbC5uYW1lcyA9IGMoIkRhdGFzZXQiLCAiVGl0bGUiKSkgJT4lCiAgICBrYWJsZV9zdHlsaW5nKAogICAgYm9vdHN0cmFwX29wdGlvbnMgPSBjKCJzdHJpcGVkIiwgImhvdmVyIiwgImNvbmRlbnNlZCIsICJyZXNwb25zaXZlIiksCiAgICBmdWxsX3dpZHRoID0gRkFMU0UsCiAgICBwb3NpdGlvbiA9ICJjZW50ZXIiCiAgKQpgYGAKCiMjIyBJbXBvcnQgw7pkYWpvdiB6IC5jc3YgYWxlYm8gLnhscwoKSmEgc29tIHNpIHp2b2xpbCDDumRhamUgeiBbQWJvc2VkZSBUaWFtaXl1OiBFbnZpcm9ubWVudGFsLCBTb2NpYWwsIGFuZCBHb3Zlcm5hbmNlIFJlcG9ydGluZyBFdmlkZW5jaW5nIEZpcm0gUGVyZm9ybWFuY2UgaW4gRW1lcmdpbmcgRWNvbm9teV17aHR0cHM6Ly9kYXRhLm1lbmRlbGV5LmNvbS9kYXRhc2V0cy83azhwamhzcndiLzF9LiBOYSBzdHLDoW5rZSBzYSBuYWNow6FkemEgc8O6Ym9yIC5EYXRhc2V0IEVTRyBhbmQgRmlybSBQZXJmb3JtYW5jZS54bHN4Liwga3RvcsO9IHNvbSBzaSBzdGlhaG9sIGEgZXhwb3J0b3ZhbCBkbyBmb3Jtw6F0dSBjc3YuIEFrbyBvZGRlxL5vdmHEjSBwb2xvxb5pZWsgc29tIHNpIHp2b2xpbCBib2Rrb8SNaWFya3UgKHNlbWljb2xvbiA7KSwgdnnFvsOtdmFtIGRlc2F0aW5uw7ogYm9ka3UgYSBuaWUgxI1pYXJrdSBhIHRpZcW+IHRleHRvdsOpIHByZW1lbm7DqSB1dsOhZHphbSBhcG9zdHJvZm1pICIuIFYgcHJ2b20gcmlhZGt1IHNhIG5hY2jDoWR6YWrDuiBuw6F6dnkgc3TEunBjb3YsIGt0b3LDqSBuZXNrw7RyIGJ1ZMO6IHZ5c3R1cG92YcWlIGFrbyBwcmVtZW5uw6kuIFRpZSBvYnNhaHVqw7ogbWVkemVyeSwgxI1vIGplIHYgesOhenZlIHByZW1lbm5laiBuZXByw61wdXN0bsOpIGEgbmFocmFkaWwgc29tIGljaCBwb2R0cmhvdsOhdGtvbSAiLiIuICAKCiFbTsOhaMS+YWQgbmEgeGxzIGRhdGFiw6F6dSBvdHZvcmVuw7ogdiB0YWJ1xL5rb3ZvbSBwcm9jZXNvcmVdKG9icmF6a3kvdWRhamV4bHMuanBnKXt3aWR0aD0xMDAlfQoKIVtOw6FoxL5hZCBuYSBjc3YgZGF0YWLDoXp1IG90dm9yZW7DuiB2IHRleHRvdm9tIHByb2Nlc29yZV0ob2JyYXpreS91ZGFqZWNzdi5qcGcpe3dpZHRoPTEwMCV9CgpQb3RvbSB1xb4gc3RhxI3DrSBpbXBvcnRvdmHFpSDDumRhamUgZG8gLmRhdGEuZnJhbWUuLCBhIHRvIG5hc2xlZG92bmUKCmBgYHtyfQp1ZGFqZSA8LSByZWFkLmNzdjIoInVkYWplL0RhdGFzZXQgRVNHIGFuZCBGaXJtIFBlcmZvcm1hbmNlLmNzdiIsaGVhZGVyPVRSVUUsc2VwPSI7IixkZWM9Ii4iKQpoZWFkKHVkYWplKSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgbmlla29sa28gcHJ2eWNoIHJpYWRrb3YKY29sbmFtZXModWRhamUpICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIG5henZ5IHByZW1lbm55Y2gKYGBgCgojIyBHcmFmeQoKCiMjIyBnZ3Bsb3QyIC0ga25pxb5uaWNhIHByZSBncmFmeQoKVsO9YmVyIGEgbsOhc2xlZG7DqSB0cmllZGVuaWUKYGBge3J9CmxpYnJhcnkoZHBseXIpCgp1ZGFqZS4yMDEzIDwtIHVkYWplICU+JQogIGZpbHRlcihZRUFSUyA9PSAyMDEzKSAlPiUKICBzZWxlY3QoUkVUVVJOLk9OLkFTU0VUUywgRVNHLklOREVYLCBERUJULlRPLkFTU0VULCBGSVJNLlNJWkUpCmBgYAoKS25pxb5uaWNhIC5nZ3Bsb3QyLiBqZSB2IHPDusSNYXNub3N0aSBuYWrEjWFzdGVqxaFpZSBwb3XFvsOtdmFuw6EgZ3JhZmlja8OhIGtuacW+bmljYSwgcHJpxI1vbSBwcmVkcHJpcHJhdmVuw6kga8OzZHkgayBqZWRub3RsaXbDvW0gb2Jyw6F6a29tIHNpIHZpZXRlIG7DoWpzxaUgdiBbUiBHcmFwaCBHYWxsZXJ5XShodHRwczovL3ItZ3JhcGgtZ2FsbGVyeS5jb20vKS4gVHUgc2kgdXZlZGllbWUgamVkbm9kdWNoxaFpZSB6IG5pY2guCgojIyMjIFNjYXR0ZXIgcGxvdAoKYGBge3J9CiMgQmFzaWMgc2NhdHRlciBwbG90CmxpYnJhcnkoZ2dwbG90MikKZ2dwbG90KHVkYWplLjIwMTMsIGFlcyh4ID0gRklSTS5TSVpFLCB5ID0gRVNHLklOREVYKSkgKyAgICAgICAgICAgICMgc3BlY2lmaWthY2lhIG9zaQogIGdlb21fcG9pbnQoKSArICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyB0eXAgZ3JhZnUgLSBzY2F0dGVycGxvdAogIHRoZW1lX21pbmltYWwoKSArCiAgbGFicyh0aXRsZSA9ICJFU0cgaW5kZXgiLCB4ID0gIlZlxL5rb3PFpSBmaXJteSIsIHkgPSAiU2NvcmUiKSAgICAgICMgb3puYWNlbmllIG9zaQpgYGAKCiMjIyMgQm94cGxvdAoKYGBge3J9CiMgQmFyIHBsb3Qgd2l0aCBncm91cGluZwpsaWJyYXJ5KGdncGxvdDIpCgpsaWJyYXJ5KGdncGxvdDIpCgpnZ3Bsb3QodWRhamUsIGFlcyh4ID0gZmFjdG9yKFlFQVJTKSwgeSA9IEVTRy5JTkRFWCkpICsgICAgICAgICMgc3BlY2lmaWthY2lhIG9zaQogIGdlb21fYm94cGxvdChmaWxsID0gImxpZ2h0Ymx1ZSIsIGNvbG9yID0gImRhcmtibHVlIikgKyAgICAgICMgdHlwIGdyYWZ1IC0gYm94cGxvdAogIGxhYnMoICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgb3puYWNlbmllIG9zaSwgbmF6b3YgZ3JhZnUKICAgIHRpdGxlID0gIkVTRyBJbmRleCBieSBZZWFycyIsCiAgICB4ID0gIlllYXIiLAogICAgeSA9ICJFU0cgSW5kZXgiCiAgKSArCiAgdGhlbWVfbWluaW1hbCgpCmBgYAoKIyBaw6FrbGFkbsOpIMWhdGF0aXN0aWt5LiAKCgojIyBrbml0ciAtIHRhYnXEvmthCgpgYGB7cn0KbGlicmFyeShkcGx5cikKbGlicmFyeShrbml0cikKCiMgU3VtbWFyaXNlIGJhc2ljIHN0YXRpc3RpY3MKZXNnLnN0YXRzIDwtIHVkYWplICU+JQogIGZpbHRlcihZRUFSUyAlaW4lIDIwMTM6MjAxNikgJT4lCiAgZ3JvdXBfYnkoWUVBUlMpICU+JQogIHN1bW1hcmlzZSgKICAgIG4gICAgID0gbigpLAogICAgbWVhbiAgPSBtZWFuKEVTRy5JTkRFWCwgbmEucm0gPSBUUlVFKSwKICAgIHNkICAgID0gc2QoRVNHLklOREVYLCBuYS5ybSA9IFRSVUUpLAogICAgbWluICAgPSBtaW4oRVNHLklOREVYLCBuYS5ybSA9IFRSVUUpLAogICAgcTI1ICAgPSBxdWFudGlsZShFU0cuSU5ERVgsIDAuMjUsIG5hLnJtID0gVFJVRSksCiAgICBtZWRpYW49IG1lZGlhbihFU0cuSU5ERVgsIG5hLnJtID0gVFJVRSksCiAgICBxNzUgICA9IHF1YW50aWxlKEVTRy5JTkRFWCwgMC43NSwgbmEucm0gPSBUUlVFKSwKICAgIG1heCAgID0gbWF4KEVTRy5JTkRFWCwgbmEucm0gPSBUUlVFKSwKICAgIC5ncm91cHMgPSAiZHJvcCIKICApCgojIENyZWF0ZSBrbml0ciB0YWJsZQprYWJsZShlc2cuc3RhdHMsIGRpZ2l0cyA9IDIsIGNhcHRpb24gPSAiQmFzaWMgc3RhdGlzdGljcyBvZiBFU0cgSW5kZXggKDIwMTPigJMyMDE2KSIpCmBgYAoKYWxlYm8ga3JhasWhaWUgdGFidcS+a3kgcyBwb21vY291IC5rYWJsZUV4dHJhLjoKCmBgYHtyfQpsaWJyYXJ5KGRwbHlyKQpsaWJyYXJ5KGtuaXRyKQpsaWJyYXJ5KGthYmxlRXh0cmEpCgojIFN1bW1hcmlzZSBiYXNpYyBzdGF0aXN0aWNzCmVzZy5zdGF0cyA8LSB1ZGFqZSAlPiUKICBmaWx0ZXIoWUVBUlMgJWluJSAyMDEzOjIwMTYpICU+JQogIGdyb3VwX2J5KFlFQVJTKSAlPiUKICBzdW1tYXJpc2UoCiAgICBuICAgICAgPSBuKCksCiAgICBtZWFuICAgPSBtZWFuKEVTRy5JTkRFWCwgbmEucm0gPSBUUlVFKSwKICAgIHNkICAgICA9IHNkKEVTRy5JTkRFWCwgbmEucm0gPSBUUlVFKSwKICAgIG1pbiAgICA9IG1pbihFU0cuSU5ERVgsIG5hLnJtID0gVFJVRSksCiAgICBxMjUgICAgPSBxdWFudGlsZShFU0cuSU5ERVgsIDAuMjUsIG5hLnJtID0gVFJVRSksCiAgICBtZWRpYW4gPSBtZWRpYW4oRVNHLklOREVYLCBuYS5ybSA9IFRSVUUpLAogICAgcTc1ICAgID0gcXVhbnRpbGUoRVNHLklOREVYLCAwLjc1LCBuYS5ybSA9IFRSVUUpLAogICAgbWF4ICAgID0gbWF4KEVTRy5JTkRFWCwgbmEucm0gPSBUUlVFKSwKICAgIC5ncm91cHMgPSAiZHJvcCIKICApCgojIENyZWF0ZSBzdHlsZWQga2FibGVFeHRyYSB0YWJsZQplc2cuc3RhdHMgJT4lCiAga2FibGUoZGlnaXRzID0gMiwgY2FwdGlvbiA9ICJCYXNpYyBzdGF0aXN0aWNzIG9mIEVTRyBJbmRleCAoMjAxM+KAkzIwMTYpIikgJT4lCiAga2FibGVfc3R5bGluZyhmdWxsX3dpZHRoID0gRkFMU0UsIGJvb3RzdHJhcF9vcHRpb25zID0gYygic3RyaXBlZCIsICJob3ZlciIsICJjb25kZW5zZWQiKSkgJT4lCiAgY29sdW1uX3NwZWMoMSwgYm9sZCA9IFRSVUUpICU+JSAgICAgICAgICAjIG1ha2UgeWVhcnMgYm9sZAogIHJvd19zcGVjKDAsIGJvbGQgPSBUUlVFLCBiYWNrZ3JvdW5kID0gIiNmMmYyZjIiKSAlPiUgICMgc3R5bGUgaGVhZGVyIHJvdwogIGFkZF9oZWFkZXJfYWJvdmUoYygiICIgPSAyLCAiRVNHIEluZGV4IFN0YXRpc3RpY3MiID0gNykpCmBgYAoKCgojIyMjIHQtdGVzdDogUG9yb3ZuYW5pZSBwcmllbWVydSBFU0cgaW5kZXh1IHYgcm9rb2NoIDIwMTMgYSAyMDE1CgpgYGB7cn0KdC50ZXN0LnJlc3VsdCA8LSB0LnRlc3QoCiAgdWRhamUkRVNHLklOREVYW3VkYWplJFlFQVJTID09IDIwMTNdLAogIHVkYWplJEVTRy5JTkRFWFt1ZGFqZSRZRUFSUyA9PSAyMDE1XQopCgpwcmludCh0LnRlc3QucmVzdWx0KQpgYGAKCgojIyMjIEFOT1ZBOiBDb21wYXJpbmcgUmVhZGluZyBTY29yZXMgQWNyb3NzIFByb2dyYW1zCgpgYGB7cn0KYW5vdmEucmVzdWx0IDwtIGFvdihFU0cuSU5ERVggfiBZRUFSUywgZGF0YSA9IHVkYWplKQpzdW1tYXJ5KGFub3ZhLnJlc3VsdCkKYGBgCgojIyMjIExpbmVhciBSZWdyZXNzaW9uOiBQcmVkaWN0aW5nIE1hdGggU2NvcmVzCgpgYGB7cn0KbW9kZWwgPC0gbG0oRVNHLklOREVYIH4gUkVUVVJOLk9OLkFTU0VUUyArIEZJUk0uU0laRSArIERFQlQuVE8uQVNTRVQsIGRhdGEgPSB1ZGFqZS4yMDEzKQpzdW1tYXJ5KG1vZGVsKQpgYGAKCgoKYGBge3J9CiMgaW5zdGFsbC5wYWNrYWdlcyhjKCJicm9vbSIsICJrYWJsZUV4dHJhIiwgImRwbHlyIiwgInN0cmluZ3IiKSkKbGlicmFyeShicm9vbSkKbGlicmFyeShkcGx5cikKbGlicmFyeShrYWJsZUV4dHJhKQpsaWJyYXJ5KHN0cmluZ3IpCgojIFlvdXIgbW9kZWwgKGFscmVhZHkgZml0dGVkKQojIG1vZGVsIDwtIGxtKEVTRy5JTkRFWCB+IFJFVFVSTi5PTi5BU1NFVFMgKyBGSVJNLlNJWkUgKyBERUJULlRPLkFTU0VULCBkYXRhID0gdWRhamUuMjAxMykKCmNvZWYudGJsIDwtIHRpZHkobW9kZWwsIGNvbmYuaW50ID0gVFJVRSkgJT4lCiAgbXV0YXRlKAogICAgdGVybSA9IHJlY29kZSh0ZXJtLAogICAgICAiKEludGVyY2VwdCkiID0gIkludGVyY2VwdCIsCiAgICAgICJSRVRVUk4uT04uQVNTRVRTIiA9ICJSZXR1cm4gb24gQXNzZXRzIiwKICAgICAgIkZJUk0uU0laRSIgPSAiRmlybSBTaXplIiwKICAgICAgIkRFQlQuVE8uQVNTRVQiID0gIkRlYnQgdG8gQXNzZXQiCiAgICApLAogICAgc3RhcnMgPSBjYXNlX3doZW4oCiAgICAgIHAudmFsdWUgPCAwLjAwMSB+ICIqKioiLAogICAgICBwLnZhbHVlIDwgMC4wMSAgfiAiKioiLAogICAgICBwLnZhbHVlIDwgMC4wNSAgfiAiKiIsCiAgICAgIHAudmFsdWUgPCAwLjEgICB+ICLCtyIsCiAgICAgIFRSVUUgICAgICAgICAgICB+ICIiCiAgICApCiAgKSAlPiUKICB0cmFuc211dGUoCiAgICBUZXJtID0gdGVybSwKICAgIEVzdGltYXRlID0gZXN0aW1hdGUsCiAgICBgU3RkLiBFcnJvcmAgPSBzdGQuZXJyb3IsCiAgICBgdCB2YWx1ZWAgPSBzdGF0aXN0aWMsCiAgICBgcCB2YWx1ZWAgPSBwLnZhbHVlLAogICAgYDk1JSBDSWAgPSBzdHJfYygiWyIsIHJvdW5kKGNvbmYubG93LCAzKSwgIiwgIiwgcm91bmQoY29uZi5oaWdoLCAzKSwgIl0iKSwKICAgIFNpZyA9IHN0YXJzCiAgKQoKY29lZi50YmwgJT4lCiAga2FibGUoCiAgICBkaWdpdHMgPSAzLAogICAgY2FwdGlvbiA9ICJPTFMgUmVncmVzc2lvbiBDb2VmZmljaWVudHMgKEVTRy5JTkRFWCB+IFJFVFVSTi5PTi5BU1NFVFMgKyBGSVJNLlNJWkUgKyBERUJULlRPLkFTU0VUKSIKICApICU+JQogIGthYmxlX3N0eWxpbmcoZnVsbF93aWR0aCA9IEZBTFNFLCBib290c3RyYXBfb3B0aW9ucyA9IGMoInN0cmlwZWQiLCAiaG92ZXIiLCAiY29uZGVuc2VkIikpICU+JQogIGNvbHVtbl9zcGVjKDEsIGJvbGQgPSBUUlVFKSAlPiUKICByb3dfc3BlYygwLCBib2xkID0gVFJVRSwgYmFja2dyb3VuZCA9ICIjZjJmMmYyIikgJT4lCiAgZm9vdG5vdGUoCiAgICBnZW5lcmFsID0gIlNpZ25pZi4gY29kZXM6ICoqKiBwPDAuMDAxLCAqKiBwPDAuMDEsICogcDwwLjA1LCDCtyBwPDAuMS4iLAogICAgdGhyZWVwYXJ0dGFibGUgPSBUUlVFCiAgKQpgYGAKCmBgYHtyfQpmaXQudGJsIDwtIGdsYW5jZShtb2RlbCkgJT4lCiAgdHJhbnNtdXRlKAogICAgYFItc3F1YXJlZGAgPSByLnNxdWFyZWQsCiAgICBgQWRqLiBSLXNxdWFyZWRgID0gYWRqLnIuc3F1YXJlZCwKICAgIGBGLXN0YXRpc3RpY2AgPSBzdGF0aXN0aWMsCiAgICBgRiBwLXZhbHVlYCA9IHAudmFsdWUsCiAgICBgQUlDYCA9IEFJQywKICAgIGBCSUNgID0gQklDLAogICAgYE51bS4gb2JzLmAgPSBub2JzCiAgKQoKZml0LnRibCAlPiUKICBrYWJsZShkaWdpdHMgPSAzLCBjYXB0aW9uID0gIk1vZGVsIEZpdCBTdGF0aXN0aWNzIikgJT4lCiAga2FibGVfc3R5bGluZyhmdWxsX3dpZHRoID0gRkFMU0UsIGJvb3RzdHJhcF9vcHRpb25zID0gYygiY29uZGVuc2VkIikpCmBgYAoKCgoKCgojIEluZm8gemRyb2plIHByZSDEj2FsxaFpZSDFoXTDumRpdW0KCiMjIyMgUiBQcm9qZWN0CgotICAgW1IgUHJvamVjdCBIb21lcGFnZV0oaHR0cHM6Ly93d3cuci1wcm9qZWN0Lm9yZy8pIC0gQmFzZSBSIGRvd25sb2FkcywKICAgIG5ld3MsIGFuZCBsZWFybmluZyByZXNvdXJjZXMKCiMjIyMgUG9zaXQKCi0gICBbUiBTdHVkaW8gRGVza3RvcF0oaHR0cHM6Ly9wb3NpdC5jby9kb3dubG9hZC9yc3R1ZGlvLWRlc2t0b3AvKSAtCiAgICBGZWF0dXJlIHJpY2ggZW52aXJvbm1lbnQgZm9yIHdvcmtpbmcgd2l0aCBkYXRhIGluIFIKLSAgIFtSIGZvciBEYXRhIFNjaWVuY2VdKGh0dHBzOi8vcjRkcy5oYWQuY28ubnovKSAtIENvbXByZWhlbnNpdmUgb25saW5lCiAgICBib29rCi0gICBbUlN0dWRpbwogICAgQ2hlYXRzaGVldHNdKGh0dHBzOi8vd3d3LnJzdHVkaW8uY29tL3Jlc291cmNlcy9jaGVhdHNoZWV0cy8pIC0gUXVpY2sKICAgIHJlZmVyZW5jZSBndWlkZXMKCiMjIyMgQ29tbXVuaXR5IFJlc291cmNlcwoKLSAgIFtSLWJsb2dnZXJzXShodHRwczovL3d3dy5yLWJsb2dnZXJzLmNvbS8pIC0gQmxvZyBhZ2dyZWdhdG9yIGZvciBSCiAgICBuZXdzIGFuZCB0dXRvcmlhbHMKLSAgIFtTdGFjayBPdmVyZmxvdyAtIFIKICAgIHRhZ10oaHR0cHM6Ly9zdGFja292ZXJmbG93LmNvbS9xdWVzdGlvbnMvdGFnZ2VkL3IpIC0gUSZBIGNvbW11bml0eQotICAgW3JkcnIuaW8gU25pcHBldHNdKGh0dHBzOi8vcmRyci5pby9zbmlwcGV0cy8pIC0gVGVzdCBSIGNvZGUgc25pcHBldHMKICAgIG9ubGluZQotICAgW0NvdXJzZXJhIC0gUgogICAgUHJvZ3JhbW1pbmddKGh0dHBzOi8vd3d3LmNvdXJzZXJhLm9yZy9sZWFybi9yLXByb2dyYW1taW5nKSAtIE9ubGluZQogICAgY291cnNlCi0gICBbRGF0YUNhbXAgLSBJbnRyb2R1Y3Rpb24gdG8KICAgIFJdKGh0dHBzOi8vd3d3LmRhdGFjYW1wLmNvbS9jb3Vyc2VzL2ZyZWUtaW50cm9kdWN0aW9uLXRvLXIpIC0KICAgIEludGVyYWN0aXZlIGxlYXJuaW5nIHBsYXRmb3JtCgo=