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

Spracované a inšpirované Notebookom Jason Locklin: Introduction to R for Education Data Analysis and Visualization

Práca s údajmi

Práca s údajmi o zamestnancoch

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

Ukážeme si prácu s dátovým typom data.frame, ktorý predstavuje tabuľku zamestnancov:

# Vytvorenie troch vektorov – Meno, Vek, Mzda (v €)
Meno = c("Peter", "Lucia", "Andrej", "Michaela")
Vek = c(28, 35, 41, 25)
Mzda = c(1450, 1980, 2500, 1320)
Deti = c(0, 1, 2, 0)

Tieto premenné spojíme do jednej tabuľky:

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

Práca s jednotlivými stĺpcami

print(udaje$Mzda)                 # vypíše stĺpec mzdy
[1] 1450 1980 2500 1320 2230
print(mean(udaje$Mzda))           # priemerná mzda
[1] 1896
print(udaje[Meno=="Lucia",])      # záznam Lucie
print(udaje[3,])                  # treti riadok
print(udaje[,2:3])                # vek a mzda
print(udaje[1,1])                 # prvá bunka tabuľky
[1] "Peter"
summary(udaje)                    # základné štatistiky
     Meno                Vek            Mzda           Deti     PracujeZDomu   
 Length:5           Min.   :25.0   Min.   :1320   Min.   :0.0   Mode :logical  
 Class :character   1st Qu.:28.0   1st Qu.:1450   1st Qu.:0.0   FALSE:2        
 Mode  :character   Median :33.0   Median :1980   Median :1.0   TRUE :3        
                    Mean   :32.4   Mean   :1896   Mean   :1.2                  
                    3rd Qu.:35.0   3rd Qu.:2230   3rd Qu.:2.0                  
                    Max.   :41.0   Max.   :2500   Max.   :3.0                  
  Oddelenie        
 Length:5          
 Class :character  
 Mode  :character  
                   
                   
                   

Pridanie nového stĺpca

# Premenná informuje, či zamestnanec pracuje z domu
PracujeZDomu <- c(TRUE, FALSE, TRUE, TRUE)
udaje <- cbind(udaje, PracujeZDomu)
print(udaje)

Pridanie nového riadku

novy.riadok <- data.frame(Meno = "Tomaš", Vek = 33, Mzda = 2230 ,Deti = 3, PracujeZDomu = 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","c","r","r"),
  caption = "Zoznam zamestnancov a ich mzdy"
#  label = NULL,
#  format.args = list(),
#  escape = TRUE,
 # ...
) %>%
      kable_styling(bootstrap_options = c("striped", "hover", "condensed", "responsive"),
    full_width = FALSE,
    position = "center")
Zoznam zamestnancov a ich mzdy
Meno Vek Mzda Deti PracujeZDomu
Peter 28 1450 0 TRUE
Lucia 35 1980 1 FALSE
Andrej 41 2500 2 TRUE
Michaela 25 1320 0 TRUE
Tomaš 33 2230 3 FALSE
NA
NA
NA
NA
NA

Moderná práca s údajmi – tidyverse

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 - Výber a triedenie údajov

.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.

  udaje %>%
  filter(Mzda > 1500) %>%
  arrange(desc(Mzda)) %>%
  kable(caption = "Zamestnanci s mzdou nad 1500 €") %>%
  kable_styling(full_width = FALSE)
Zamestnanci s mzdou nad 1500 €
Meno Vek Mzda Deti PracujeZDomu
Andrej 41 2500 2 TRUE
Tomaš 33 2230 3 FALSE
Lucia 35 1980 1 FALSE

Zoskupenie a sumarizácia

Oddelenie <- c("IT","Marketing","IT","HR","Marketing")
udaje <- cbind(udaje, Oddelenie)

udaje %>%
  group_by(Oddelenie) %>%
  summarise(
    PriemernaMzda = mean(Mzda),
    Pocet = n()
  ) %>%
  kable(caption = "Priemerná mzda podľa oddelenia") %>%
  kable_styling(full_width = FALSE)
Priemerná mzda podľa oddelenia
Oddelenie PriemernaMzda Pocet
HR 1320 1
IT 1975 2
Marketing 2105 2

Vytváranie novej premennej

udaje %>%
  mutate(
    SkupinaMzdy = case_when(
      Mzda >= 2200 ~ "Vysoká",
      Mzda >= 1600 ~ "Stredná",
      TRUE ~ "Nízka"
    ),
    RokyPo30 = if_else(Vek > 30, Vek - 30, 0)
  ) %>%
  kable(caption = "Zamestnanci s kategóriami mzdy a veku") %>%
  kable_styling(full_width = FALSE)
Zamestnanci s kategóriami mzdy a veku
Meno Vek Mzda Deti PracujeZDomu Oddelenie SkupinaMzdy RokyPo30
Peter 28 1450 0 TRUE IT Nízka 0
Lucia 35 1980 1 FALSE Marketing Stredná 5
Andrej 41 2500 2 TRUE IT Vysoká 11
Michaela 25 1320 0 TRUE HR Nízka 0
Tomaš 33 2230 3 FALSE Marketing Vysoká 3

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
Dataset Title
AirPassengers Monthly Airline Passenger Numbers 1949-1960
BJsales Sales Data with Leading Indicator
BJsales.lead (BJsales) Sales Data with Leading Indicator
BOD Biochemical Oxygen Demand
CO2 Carbon Dioxide Uptake in Grass Plants
ChickWeight Weight versus age of chicks on different diets
DNase Elisa assay of DNase
EuStockMarkets Daily Closing Prices of Major European Stock Indices, 1991-1998
Formaldehyde Determination of Formaldehyde
HairEyeColor Hair and Eye Color of Statistics Students
Harman23.cor Harman Example 2.3
Harman74.cor Harman Example 7.4
Indometh Pharmacokinetics of Indomethacin
InsectSprays Effectiveness of Insect Sprays
JohnsonJohnson Quarterly Earnings per Johnson & Johnson Share
LakeHuron Level of Lake Huron 1875-1972
LifeCycleSavings Intercountry Life-Cycle Savings Data
Loblolly Growth of Loblolly Pine Trees
Nile Flow of the River Nile
Orange Growth of Orange Trees
# 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"
  )
Dataset Title
admnrev admnrev
affairs affairs
airfare airfare
alcohol alcohol
apple apple
approval approval
athlet1 athlet1
athlet2 athlet2
attend attend
audit audit
barium barium
beauty beauty
benefits benefits
beveridge beveridge
big9salary big9salary
bwght bwght
bwght2 bwght2
campus campus
card card
catholic catholic

Import údajov z .csv alebo .xls

Ja som si zvolila ú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”.”.

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(ggplot2)

ggplot(udaje, aes(x = Oddelenie, y = Mzda, fill = Oddelenie)) +
  geom_boxplot() +
  theme_minimal() +
  labs(title = "Rozdelenie miezd podľa oddelenia",
       x = "Oddelenie",
       y = "Mzda (€)")

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 – Vek vs. Mzda

ggplot(udaje, aes(x = Vek, y = Mzda, color = Oddelenie)) +
  geom_point(size = 3) +
  theme_minimal() +
  labs(title = "Vzťah medzi vekom a mzdou",
       x = "Vek zamestnanca",
       y = "Mzda (€)")

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)

esg.stats <- udaje %>%
  group_by(Oddelenie) %>%
  summarise(
    n = n(),
    priemer = mean(Mzda),
    sd = sd(Mzda),
    min = min(Mzda),
    median = median(Mzda),
    max = max(Mzda),
    .groups = "drop"
  )

kable(esg.stats, digits = 2, caption = "Základné štatistiky miezd podľa oddelenia") %>%
  kable_styling(full_width = FALSE)
Základné štatistiky miezd podľa oddelenia
Oddelenie n priemer sd min median max
HR 1 1320 NA 1320 1320 1320
IT 2 1975 742.46 1450 1975 2500
Marketing 2 2105 176.78 1980 2105 2230

t-test: porovnanie IT vs Marketing

t.test(
  udaje$Mzda[udaje$Oddelenie == "IT"],
  udaje$Mzda[udaje$Oddelenie == "Marketing"]
)

    Welch Two Sample t-test

data:  udaje$Mzda[udaje$Oddelenie == "IT"] and udaje$Mzda[udaje$Oddelenie == "Marketing"]
t = -0.24089, df = 1.113, p-value = 0.8463
alternative hypothesis: true difference in means is not equal to 0
95 percent confidence interval:
 -5542.164  5282.164
sample estimates:
mean of x mean of y 
     1975      2105 
print(t.test)
function (x, ...) 
UseMethod("t.test")
<bytecode: 0x6535c3809510>
<environment: namespace:stats>

Linear Regression: Predicting Math Scores

model <- lm(Mzda ~ Vek + PracujeZDomu, data = udaje)
summary(model)

Call:
lm(formula = Mzda ~ Vek + PracujeZDomu, data = udaje)

Residuals:
      1       2       3       4       5 
 -62.95 -198.11   36.57   26.39  198.11 

Coefficients:
                 Estimate Std. Error t value Pr(>|t|)  
(Intercept)       -380.86     595.07  -0.640   0.5877  
Vek                 73.11      16.97   4.308   0.0499 *
PracujeZDomuTRUE  -153.36     193.01  -0.795   0.5102  
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 205.5 on 2 degrees of freedom
Multiple R-squared:  0.9167,    Adjusted R-squared:  0.8334 
F-statistic:    11 on 2 and 2 DF,  p-value: 0.08332
# 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
  )
OLS Regression Coefficients (ESG.INDEX ~ RETURN.ON.ASSETS + FIRM.SIZE + DEBT.TO.ASSET)
Term Estimate Std. Error t value p value 95% CI Sig
Intercept -380.864 595.071 -0.640 0.588 [-2941.249, 2179.522]
Vek 73.114 16.972 4.308 0.050 [0.089, 146.138] *
PracujeZDomuTRUE -153.364 193.014 -0.795 0.510 [-983.837, 677.11]
Note:
Signif. codes: *** p<0.001, ** p<0.01, * p<0.05, · p<0.1.
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"))
Model Fit Statistics
R-squared Adj. R-squared F-statistic F p-value AIC BIC Num. obs.
0.917 0.833 11.002 0.083 70.864 69.302 5

Info zdroje pre ďalšie štúdium

R Project

Posit

Community Resources

LS0tCnRpdGxlOiAiUiBOb3RlYm9vayIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQoKCi0tLQp0aXRsZTogIlByw6FjYSBzIGRhdGFiw6F6b3UiCmF1dGhvcjogIkFuYXN0YXNpeWEgWnlsZXZpY2giCmRhdGU6ICJPa3RvYmVyIDIwMjUiCm91dHB1dDogCiAgaHRtbF9ub3RlYm9vazoKICAgIHRvYzogdHJ1ZQogICAgdG9jX2Zsb2F0OiB0cnVlCiAgICB0aGVtZTogdW5pdGVkCiAgICBoaWdobGlnaHQ6IHRhbmdvCmVkaXRvcl9vcHRpb25zOiAKICBtYXJrZG93bjogCiAgICB3cmFwOiA3MgotLS0KCmBgYHtyfQprbml0cjo6b3B0c19jaHVuayRzZXQoCiAgICBlY2hvID0gVFJVRSwKICAgIG1lc3NhZ2UgPSBGQUxTRSwKICAgIHdhcm5pbmcgPSBGQUxTRQopCmBgYAoKU3ByYWNvdmFuw6kgYSBpbsWhcGlyb3ZhbsOpIE5vdGVib29rb20KW0phc29uIExvY2tsaW46IEludHJvZHVjdGlvbiB0byBSIGZvciBFZHVjYXRpb24gRGF0YSBBbmFseXNpcyBhbmQgVmlzdWFsaXphdGlvbl0oaHR0cDovL3JwdWJzLmNvbS9qYXNvbkwvTGFuZEwpe3RhcmdldD0iX2JsYW5rIiByZWw9Im5vb3BlbmVyIn0KCgojIFByw6FjYSBzIMO6ZGFqbWkKCiMjIFByw6FjYSBzIMO6ZGFqbWkgbyB6YW1lc3RuYW5jb2NoCgpQcmUgcHLDoWN1IHMgw7pkYWptaSAoZGF0YWLDoXpvdSkgcG91xb7DrXZhbWUgbmFqxI1hc3RlasWhaWUgZMOhdG92w70gdHlwIC5kYXRhLmZyYW1lLi4gSmUgdG8gdGFidcS+a2EsIGt0b3LDoSBwb3pvc3TDoXZhIHpvIHN0xLpwY292IHJvemxpxI1uw71jaCB0eXBvdi4gSmVkZW4gcmlhZG9rIHByaXRvbSBwcmVkc3RhdnVqZSBqZWRlbiB6w6F6bmFtIGRhdGFiw6F6eS4KCiMjIyBQcsOta2xhZAoKIyBVa8Ohxb5lbWUgc2kgcHLDoWN1IHMgZMOhdG92w71tIHR5cG9tIGRhdGEuZnJhbWUsIGt0b3LDvSBwcmVkc3RhdnVqZSB0YWJ1xL5rdSB6YW1lc3RuYW5jb3Y6CmBgYHtyfQojIFZ5dHZvcmVuaWUgdHJvY2ggdmVrdG9yb3Yg4oCTIE1lbm8sIFZlaywgTXpkYSAodiDigqwpCk1lbm8gPSBjKCJQZXRlciIsICJMdWNpYSIsICJBbmRyZWoiLCAiTWljaGFlbGEiKQpWZWsgPSBjKDI4LCAzNSwgNDEsIDI1KQpNemRhID0gYygxNDUwLCAxOTgwLCAyNTAwLCAxMzIwKQpEZXRpID0gYygwLCAxLCAyLCAwKQpgYGAKCgojIFRpZXRvIHByZW1lbm7DqSBzcG9qw61tZSBkbyBqZWRuZWogdGFidcS+a3k6CgpgYGB7cn0KdWRhamUgPC0gZGF0YS5mcmFtZShNZW5vLCBWZWssIE16ZGEsIERldGkpCnByaW50KHVkYWplKQpgYGAKCgojIFByw6FjYSBzIGplZG5vdGxpdsO9bWkgc3TEunBjYW1pCgpgYGB7cn0KcHJpbnQodWRhamUkTXpkYSkgICAgICAgICAgICAgICAgICMgdnlww63FoWUgc3TEunBlYyBtemR5CnByaW50KG1lYW4odWRhamUkTXpkYSkpICAgICAgICAgICAjIHByaWVtZXJuw6EgbXpkYQpwcmludCh1ZGFqZVtNZW5vPT0iTHVjaWEiLF0pICAgICAgIyB6w6F6bmFtIEx1Y2llCnByaW50KHVkYWplWzMsXSkgICAgICAgICAgICAgICAgICAjIHRyZXRpIHJpYWRvawpwcmludCh1ZGFqZVssMjozXSkgICAgICAgICAgICAgICAgIyB2ZWsgYSBtemRhCnByaW50KHVkYWplWzEsMV0pICAgICAgICAgICAgICAgICAjIHBydsOhIGJ1bmthIHRhYnXEvmt5CnN1bW1hcnkodWRhamUpICAgICAgICAgICAgICAgICAgICAjIHrDoWtsYWRuw6kgxaF0YXRpc3Rpa3kKYGBgCgojIFByaWRhbmllIG5vdsOpaG8gc3TEunBjYQpgYGB7cn0KIyBQcmVtZW5uw6EgaW5mb3JtdWplLCDEjWkgemFtZXN0bmFuZWMgcHJhY3VqZSB6IGRvbXUKUHJhY3VqZVpEb211IDwtIGMoVFJVRSwgRkFMU0UsIFRSVUUsIFRSVUUpCnVkYWplIDwtIGNiaW5kKHVkYWplLCBQcmFjdWplWkRvbXUpCnByaW50KHVkYWplKQpgYGAKCiMgUHJpZGFuaWUgbm92w6lobyByaWFka3UKCmBgYHtyfQpub3Z5LnJpYWRvayA8LSBkYXRhLmZyYW1lKE1lbm8gPSAiVG9tYcWhIiwgVmVrID0gMzMsIE16ZGEgPSAyMjMwICxEZXRpID0gMywgUHJhY3VqZVpEb211ID0gRkFMU0UpCgp1ZGFqZSA8LSByYmluZCh1ZGFqZSwgbm92eS5yaWFkb2spCnByaW50KHVkYWplKQpgYGAKCiMjIyBUYWJ1xL5reSB2IHByb3N0cmVkw60ga2FibGVleHRyYQoKCmBgYHtyfQpsaWJyYXJ5KGtuaXRyKQpsaWJyYXJ5KGthYmxlRXh0cmEpCmthYmxlKAogIHVkYWplLAojICBmb3JtYXQsCmRpZ2l0cyA9IDIsCiMgIHJvdy5uYW1lcyA9IE5BLAojICBjb2wubmFtZXMgPSBOQSwKICBhbGlnbj1jKCJsIiwiYyIsImMiLCJyIiwiciIpLAogIGNhcHRpb24gPSAiWm96bmFtIHphbWVzdG5hbmNvdiBhIGljaCBtemR5IgojICBsYWJlbCA9IE5VTEwsCiMgIGZvcm1hdC5hcmdzID0gbGlzdCgpLAojICBlc2NhcGUgPSBUUlVFLAogIyAuLi4KKSAlPiUKICAgICAga2FibGVfc3R5bGluZyhib290c3RyYXBfb3B0aW9ucyA9IGMoInN0cmlwZWQiLCAiaG92ZXIiLCAiY29uZGVuc2VkIiwgInJlc3BvbnNpdmUiKSwKICAgIGZ1bGxfd2lkdGggPSBGQUxTRSwKICAgIHBvc2l0aW9uID0gImNlbnRlciIpCgpgYGAKCiMjIE1vZGVybsOhIHByw6FjYSBzIMO6ZGFqbWkg4oCTIHRpZHl2ZXJzZQoKVGlkeXZlcnNlIGplIHPDumJvciBrbmnFvm7DrWMsIGt0b3LDqSBtYWrDuiB6amVkbm9kdcWhacWlIHByw6FjdSBzIMO6ZGFqbWkuIE1hasO6IGplZG5vdG7DvSBrb211bmlrYcSNbsO9IMWhdGFuZGFyZCwgdnrDoWpvbW5lIHNhIGRvcGzFiHVqw7ouCgpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQpsaWJyYXJ5KHRpZHl2ZXJzZSkKYGBgCgojIyMgIGRwbHlyIC0gVsO9YmVyIGEgdHJpZWRlbmllIMO6ZGFqb3YKCi5kcGx5ci4gcG9za3l0dWplIHrDoWtsYWRuw6kgbW/Fvm5vc3RpIG1hbmlwdWzDoWNpZSBzIMO6ZGFqbWksIGFrbyBuYXByLjogCgoxLiBmaWx0ZXIoKTogdnliZXLDoSByaWFka3kgCgoxLiBzZWxlY3QoKTogdnliZXLDoSBzdMS6cGNlIAoKMS4gbXV0YXRlKCk6IHZ5dHbDoXJhIG5vdsOpIHN0xLpwY2UgdGFidcS+a3kgCgoxLiBhcnJhbmdlKCk6IHRyaWVkaSByaWFka3kgCgoxLiBzdW1tYXJpc2UoKTogc3VtYXJpenVqZQoKViBuYXNsZWRvdm5laiB1a8Ohxb5rZSB2eXXFvmlqZW1lIHR6di4gLnBpcGVzLiAlPiUgYWxlYm8gJTwlIHVtb8W+xYh1amUgcG9zaWVsYcWlIHbDvXNsZWRreSB6IGplZG5laiBmdW5rY2llIHByaWFtbyBkbyB2b2xhbmllIG5hc2xlZG92bmVqIGZ1bmtjaWUuIFRvIHVtb8W+xYh1amUgxL5haMWhaXUgxI1pdGF0ZcS+bm9zxaUga8OzZG92LCBrb252ZW5jaWEgc2EgdWphbGEgYSBtw6EgxaFpcm9rw6kgcG91xb5pdGllLgoKCmBgYHtyfQogIHVkYWplICU+JQogIGZpbHRlcihNemRhID4gMTUwMCkgJT4lCiAgYXJyYW5nZShkZXNjKE16ZGEpKSAlPiUKICBrYWJsZShjYXB0aW9uID0gIlphbWVzdG5hbmNpIHMgbXpkb3UgbmFkIDE1MDAg4oKsIikgJT4lCiAga2FibGVfc3R5bGluZyhmdWxsX3dpZHRoID0gRkFMU0UpCmBgYAoKIyMjIyBab3NrdXBlbmllIGEgc3VtYXJpesOhY2lhCgpgYGB7cn0KT2RkZWxlbmllIDwtIGMoIklUIiwiTWFya2V0aW5nIiwiSVQiLCJIUiIsIk1hcmtldGluZyIpCnVkYWplIDwtIGNiaW5kKHVkYWplLCBPZGRlbGVuaWUpCgp1ZGFqZSAlPiUKICBncm91cF9ieShPZGRlbGVuaWUpICU+JQogIHN1bW1hcmlzZSgKICAgIFByaWVtZXJuYU16ZGEgPSBtZWFuKE16ZGEpLAogICAgUG9jZXQgPSBuKCkKICApICU+JQogIGthYmxlKGNhcHRpb24gPSAiUHJpZW1lcm7DoSBtemRhIHBvZMS+YSBvZGRlbGVuaWEiKSAlPiUKICBrYWJsZV9zdHlsaW5nKGZ1bGxfd2lkdGggPSBGQUxTRSkKYGBgCgojIyMjIFZ5dHbDoXJhbmllIG5vdmVqIHByZW1lbm5lagoKYGBge3J9CnVkYWplICU+JQogIG11dGF0ZSgKICAgIFNrdXBpbmFNemR5ID0gY2FzZV93aGVuKAogICAgICBNemRhID49IDIyMDAgfiAiVnlzb2vDoSIsCiAgICAgIE16ZGEgPj0gMTYwMCB+ICJTdHJlZG7DoSIsCiAgICAgIFRSVUUgfiAiTsOtemthIgogICAgKSwKICAgIFJva3lQbzMwID0gaWZfZWxzZShWZWsgPiAzMCwgVmVrIC0gMzAsIDApCiAgKSAlPiUKICBrYWJsZShjYXB0aW9uID0gIlphbWVzdG5hbmNpIHMga2F0ZWfDs3JpYW1pIG16ZHkgYSB2ZWt1IikgJT4lCiAga2FibGVfc3R5bGluZyhmdWxsX3dpZHRoID0gRkFMU0UpCmBgYAoKCiMjIEltcG9ydCDDumRham92IHogb3R2LiBkYXRhYsOhegoKCjEuICpNZW5kZWxleSBEYXRhKiBUdXRvIHNhIGRvc3RhbmVtZSB6IFtNZW5kZWxleSBEYXRhXShodHRwczovL2RhdGEubWVuZGVsZXkuY29tLyl7dGFyZ2V0PSIuYmxhbmsiIHJlbD0ibm9vcGVuZXIifSwga2RlIHNpIMO6ZGFqZSB2aWV0ZSB2b8S+bmUgc3RpYWhuw7rFpS4gw5pkYWplIHNhIHZ6xaVhaHVqw7ogayB1xb4gcHVibGlrb3ZhbsO9bSDEjWzDoW5rb20gdm8gdnlkYXZhdGXEvnN0dmUgRWxzZXZpZXIuIFbDvWJlciBzYSBkw6EgdXJvYmnFpSBqZWRub2R1Y2hvIHphZGFuw61tIGvEvsO6xI1vdsO9Y2ggc2xvdi4KMi4gKkthZ2dsZSBEYXRhKiAgIFR1dG8gc2EgZG9zdGFuZW1lIHogW0thZ2dsZSBEYXRhc2V0c10oaHR0cHM6Ly93d3cua2FnZ2xlLmNvbS9kYXRhc2V0cyl7dGFyZ2V0PSIuYmxhbmsiIHJlbD0ibm9vcGVuZXIifSwga2RlIHNpIMO6ZGFqZSB2aWV0ZSB2b8S+bmUgc3RpYWhuw7rFpS4gw5pkYWplIHNhIHZ6xaVhaHVqw7ogayBwcm9qZWt0b20gcG9kcG9yb3ZhbsO9bSBLYWdnbGUuIFbDvWJlciBzYSBkw6EgdXJvYmnFpSBqZWRub2R1Y2hvIHphZGFuw61tIGvEvsO6xI1vdsO9Y2ggc2xvdi4KMy4gRGF0YWLDoXp5IGtuacW+bsOtYyBSIC0gLmxpYnJhcnkoZGF0YXNldHMpLiBhbGVibyAubGlicmFyeSh3b29sZHJpZGdlKS4gYWxlIGFqIGluw6kgLSBzdGHEjcOtIHNpIGRhxaUgcHLDrWtheiBkYXRhKCkKCmBgYHtyfQpsaWJyYXJ5KGRhdGFzZXRzKQojIGRhdGFzZXRzIGF2YWlsYWJsZSBpbiB0aGUgJ2RhdGFzZXRzJyBwYWNrYWdlIC0gbmFzbGVkb3ZuZSBrb2R5IHphIG1uYSB1cm9iaWwgQ2hhdCBHUFQKZHMgPC0gYXMuZGF0YS5mcmFtZSh1dGlsczo6ZGF0YShwYWNrYWdlID0gImRhdGFzZXRzIikkcmVzdWx0cylbLCBjKCJJdGVtIiwiVGl0bGUiKV0Ka25pdHI6OmthYmxlKGhlYWQoZHMsIDIwKSwgY29sLm5hbWVzID0gYygiRGF0YXNldCIsICJUaXRsZSIpKSAgICMgcHJ2eWNoIDIwIGRhdGFiYXoKIyBrbml6bmljYSBkYXRhc2V0cyBvYnNhaHVqZSBkYXRhYmF6dSBuYXp2YW51IENPMi4gTW96ZW1lIHNhIG5hIG51IG9kdm9sYXZhdCBuYXNsZWRvdm5lLCBha28gbmFwci4gCmhlYWQoQ08yKQpgYGAKCk3DtMW+ZW1lIHBvdcW+acWlIGFqIGRhdGFiw6F6dSB1csSNZW7DuiBwcmUgZWtvYm9tZXRyaXUgLSBwYWNrYWdlIFdvb2xkcmlkZ2UKCmBgYHtyfQojIGluc3RhbGwucGFja2FnZXMoIndvb2xkcmlkZ2UiKQpsaWJyYXJ5KHdvb2xkcmlkZ2UpCmRzIDwtIGFzLmRhdGEuZnJhbWUodXRpbHM6OmRhdGEocGFja2FnZSA9ICJ3b29sZHJpZGdlIikkcmVzdWx0cylbLCBjKCJJdGVtIiwiVGl0bGUiKV0Ka25pdHI6OmthYmxlKGhlYWQoZHMsIDIwKSwgY29sLm5hbWVzID0gYygiRGF0YXNldCIsICJUaXRsZSIpKSAlPiUKICAgIGthYmxlX3N0eWxpbmcoCiAgICBib290c3RyYXBfb3B0aW9ucyA9IGMoInN0cmlwZWQiLCAiaG92ZXIiLCAiY29uZGVuc2VkIiwgInJlc3BvbnNpdmUiKSwKICAgIGZ1bGxfd2lkdGggPSBGQUxTRSwKICAgIHBvc2l0aW9uID0gImNlbnRlciIKICApCmBgYAoKIyMjIEltcG9ydCDDumRham92IHogLmNzdiBhbGVibyAueGxzCgpKYSBzb20gc2kgenZvbGlsYSDDumRhamUgeiBbQWJvc2VkZSBUaWFtaXl1OiBFbnZpcm9ubWVudGFsLCBTb2NpYWwsIGFuZCBHb3Zlcm5hbmNlIFJlcG9ydGluZyBFdmlkZW5jaW5nIEZpcm0gUGVyZm9ybWFuY2UgaW4gRW1lcmdpbmcgRWNvbm9teV17aHR0cHM6Ly9kYXRhLm1lbmRlbGV5LmNvbS9kYXRhc2V0cy83azhwamhzcndiLzF9LiBOYSBzdHLDoW5rZSBzYSBuYWNow6FkemEgc8O6Ym9yIC5EYXRhc2V0IEVTRyBhbmQgRmlybSBQZXJmb3JtYW5jZS54bHN4Liwga3RvcsO9IHNvbSBzaSBzdGlhaG9sIGEgZXhwb3J0b3ZhbCBkbyBmb3Jtw6F0dSBjc3YuIEFrbyBvZGRlxL5vdmHEjSBwb2xvxb5pZWsgc29tIHNpIHp2b2xpbCBib2Rrb8SNaWFya3UgKHNlbWljb2xvbiA7KSwgdnnFvsOtdmFtIGRlc2F0aW5uw7ogYm9ka3UgYSBuaWUgxI1pYXJrdSBhIHRpZcW+IHRleHRvdsOpIHByZW1lbm7DqSB1dsOhZHphbSBhcG9zdHJvZm1pICIuIFYgcHJ2b20gcmlhZGt1IHNhIG5hY2jDoWR6YWrDuiBuw6F6dnkgc3TEunBjb3YsIGt0b3LDqSBuZXNrw7RyIGJ1ZMO6IHZ5c3R1cG92YcWlIGFrbyBwcmVtZW5uw6kuIFRpZSBvYnNhaHVqw7ogbWVkemVyeSwgxI1vIGplIHYgesOhenZlIHByZW1lbm5laiBuZXByw61wdXN0bsOpIGEgbmFocmFkaWwgc29tIGljaCBwb2R0cmhvdsOhdGtvbSAiLiIuICAKClBvdG9tIHXFviBzdGHEjcOtIGltcG9ydG92YcWlIMO6ZGFqZSBkbyAuZGF0YS5mcmFtZS4sIGEgdG8gbmFzbGVkb3ZuZQoKYGBge3J9CnVkYWplIDwtIHJlYWQuY3N2MigidWRhamUvRGF0YXNldCBFU0cgYW5kIEZpcm0gUGVyZm9ybWFuY2UuY3N2IixoZWFkZXI9VFJVRSxzZXA9IjsiLGRlYz0iLiIpCmhlYWQodWRhamUpICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBuaWVrb2xrbyBwcnZ5Y2ggcmlhZGtvdgpjb2xuYW1lcyh1ZGFqZSkgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgbmF6dnkgcHJlbWVubnljaApgYGAKCiMjIEdyYWZ5CgoKIyMjIGdncGxvdDIgLSBrbmnFvm5pY2EgcHJlIGdyYWZ5CgpWw71iZXIgYSBuw6FzbGVkbsOpIHRyaWVkZW5pZQpgYGB7cn0KbGlicmFyeShnZ3Bsb3QyKQoKZ2dwbG90KHVkYWplLCBhZXMoeCA9IE9kZGVsZW5pZSwgeSA9IE16ZGEsIGZpbGwgPSBPZGRlbGVuaWUpKSArCiAgZ2VvbV9ib3hwbG90KCkgKwogIHRoZW1lX21pbmltYWwoKSArCiAgbGFicyh0aXRsZSA9ICJSb3pkZWxlbmllIG1pZXpkIHBvZMS+YSBvZGRlbGVuaWEiLAogICAgICAgeCA9ICJPZGRlbGVuaWUiLAogICAgICAgeSA9ICJNemRhICjigqwpIikKYGBgCgpLbmnFvm5pY2EgLmdncGxvdDIuIGplIHYgc8O6xI1hc25vc3RpIG5hasSNYXN0ZWrFoWllIHBvdcW+w612YW7DoSBncmFmaWNrw6Ega25pxb5uaWNhLCBwcmnEjW9tIHByZWRwcmlwcmF2ZW7DqSBrw7NkeSBrIGplZG5vdGxpdsO9bSBvYnLDoXprb20gc2kgdmlldGUgbsOhanPFpSB2IFtSIEdyYXBoIEdhbGxlcnldKGh0dHBzOi8vci1ncmFwaC1nYWxsZXJ5LmNvbS8pLiBUdSBzaSB1dmVkaWVtZSBqZWRub2R1Y2jFoWllIHogbmljaC4KCiMjIyMgU2NhdHRlciBwbG90IOKAkyBWZWsgdnMuIE16ZGEKCmBgYHtyfQpnZ3Bsb3QodWRhamUsIGFlcyh4ID0gVmVrLCB5ID0gTXpkYSwgY29sb3IgPSBPZGRlbGVuaWUpKSArCiAgZ2VvbV9wb2ludChzaXplID0gMykgKwogIHRoZW1lX21pbmltYWwoKSArCiAgbGFicyh0aXRsZSA9ICJWesWlYWggbWVkemkgdmVrb20gYSBtemRvdSIsCiAgICAgICB4ID0gIlZlayB6YW1lc3RuYW5jYSIsCiAgICAgICB5ID0gIk16ZGEgKOKCrCkiKQpgYGAKCiMgWsOha2xhZG7DqSDFoXRhdGlzdGlreS4gCgoKIyMga25pdHIgLSB0YWJ1xL5rYQoKYGBge3J9CmxpYnJhcnkoZHBseXIpCmxpYnJhcnkoa25pdHIpCgojIFN1bW1hcmlzZSBiYXNpYyBzdGF0aXN0aWNzCmVzZy5zdGF0cyA8LSB1ZGFqZSAlPiUKICBmaWx0ZXIoWUVBUlMgJWluJSAyMDEzOjIwMTYpICU+JQogIGdyb3VwX2J5KFlFQVJTKSAlPiUKICBzdW1tYXJpc2UoCiAgICBuICAgICA9IG4oKSwKICAgIG1lYW4gID0gbWVhbihFU0cuSU5ERVgsIG5hLnJtID0gVFJVRSksCiAgICBzZCAgICA9IHNkKEVTRy5JTkRFWCwgbmEucm0gPSBUUlVFKSwKICAgIG1pbiAgID0gbWluKEVTRy5JTkRFWCwgbmEucm0gPSBUUlVFKSwKICAgIHEyNSAgID0gcXVhbnRpbGUoRVNHLklOREVYLCAwLjI1LCBuYS5ybSA9IFRSVUUpLAogICAgbWVkaWFuPSBtZWRpYW4oRVNHLklOREVYLCBuYS5ybSA9IFRSVUUpLAogICAgcTc1ICAgPSBxdWFudGlsZShFU0cuSU5ERVgsIDAuNzUsIG5hLnJtID0gVFJVRSksCiAgICBtYXggICA9IG1heChFU0cuSU5ERVgsIG5hLnJtID0gVFJVRSksCiAgICAuZ3JvdXBzID0gImRyb3AiCiAgKQoKIyBDcmVhdGUga25pdHIgdGFibGUKa2FibGUoZXNnLnN0YXRzLCBkaWdpdHMgPSAyLCBjYXB0aW9uID0gIkJhc2ljIHN0YXRpc3RpY3Mgb2YgRVNHIEluZGV4ICgyMDEz4oCTMjAxNikiKQpgYGAKCmFsZWJvIGtyYWrFoWllIHRhYnXEvmt5IHMgcG9tb2NvdSAua2FibGVFeHRyYS46CgpgYGB7cn0KbGlicmFyeShkcGx5cikKbGlicmFyeShrbml0cikKbGlicmFyeShrYWJsZUV4dHJhKQoKZXNnLnN0YXRzIDwtIHVkYWplICU+JQogIGdyb3VwX2J5KE9kZGVsZW5pZSkgJT4lCiAgc3VtbWFyaXNlKAogICAgbiA9IG4oKSwKICAgIHByaWVtZXIgPSBtZWFuKE16ZGEpLAogICAgc2QgPSBzZChNemRhKSwKICAgIG1pbiA9IG1pbihNemRhKSwKICAgIG1lZGlhbiA9IG1lZGlhbihNemRhKSwKICAgIG1heCA9IG1heChNemRhKSwKICAgIC5ncm91cHMgPSAiZHJvcCIKICApCgprYWJsZShlc2cuc3RhdHMsIGRpZ2l0cyA9IDIsIGNhcHRpb24gPSAiWsOha2xhZG7DqSDFoXRhdGlzdGlreSBtaWV6ZCBwb2TEvmEgb2RkZWxlbmlhIikgJT4lCiAga2FibGVfc3R5bGluZyhmdWxsX3dpZHRoID0gRkFMU0UpCmBgYAoKCgojIyMjIHQtdGVzdDogcG9yb3ZuYW5pZSBJVCB2cyBNYXJrZXRpbmcKCmBgYHtyfQp0LnRlc3QoCiAgdWRhamUkTXpkYVt1ZGFqZSRPZGRlbGVuaWUgPT0gIklUIl0sCiAgdWRhamUkTXpkYVt1ZGFqZSRPZGRlbGVuaWUgPT0gIk1hcmtldGluZyJdCikKCnByaW50KHQudGVzdCkKYGBgCgojIyMjIExpbmVhciBSZWdyZXNzaW9uOiBQcmVkaWN0aW5nIE1hdGggU2NvcmVzCgpgYGB7cn0KbW9kZWwgPC0gbG0oTXpkYSB+IFZlayArIFByYWN1amVaRG9tdSwgZGF0YSA9IHVkYWplKQpzdW1tYXJ5KG1vZGVsKQpgYGAKCgoKYGBge3J9CiMgaW5zdGFsbC5wYWNrYWdlcyhjKCJicm9vbSIsICJrYWJsZUV4dHJhIiwgImRwbHlyIiwgInN0cmluZ3IiKSkKbGlicmFyeShicm9vbSkKbGlicmFyeShkcGx5cikKbGlicmFyeShrYWJsZUV4dHJhKQpsaWJyYXJ5KHN0cmluZ3IpCgojIFlvdXIgbW9kZWwgKGFscmVhZHkgZml0dGVkKQojIG1vZGVsIDwtIGxtKEVTRy5JTkRFWCB+IFJFVFVSTi5PTi5BU1NFVFMgKyBGSVJNLlNJWkUgKyBERUJULlRPLkFTU0VULCBkYXRhID0gdWRhamUuMjAxMykKCmNvZWYudGJsIDwtIHRpZHkobW9kZWwsIGNvbmYuaW50ID0gVFJVRSkgJT4lCiAgbXV0YXRlKAogICAgdGVybSA9IHJlY29kZSh0ZXJtLAogICAgICAiKEludGVyY2VwdCkiID0gIkludGVyY2VwdCIsCiAgICAgICJSRVRVUk4uT04uQVNTRVRTIiA9ICJSZXR1cm4gb24gQXNzZXRzIiwKICAgICAgIkZJUk0uU0laRSIgPSAiRmlybSBTaXplIiwKICAgICAgIkRFQlQuVE8uQVNTRVQiID0gIkRlYnQgdG8gQXNzZXQiCiAgICApLAogICAgc3RhcnMgPSBjYXNlX3doZW4oCiAgICAgIHAudmFsdWUgPCAwLjAwMSB+ICIqKioiLAogICAgICBwLnZhbHVlIDwgMC4wMSAgfiAiKioiLAogICAgICBwLnZhbHVlIDwgMC4wNSAgfiAiKiIsCiAgICAgIHAudmFsdWUgPCAwLjEgICB+ICLCtyIsCiAgICAgIFRSVUUgICAgICAgICAgICB+ICIiCiAgICApCiAgKSAlPiUKICB0cmFuc211dGUoCiAgICBUZXJtID0gdGVybSwKICAgIEVzdGltYXRlID0gZXN0aW1hdGUsCiAgICBgU3RkLiBFcnJvcmAgPSBzdGQuZXJyb3IsCiAgICBgdCB2YWx1ZWAgPSBzdGF0aXN0aWMsCiAgICBgcCB2YWx1ZWAgPSBwLnZhbHVlLAogICAgYDk1JSBDSWAgPSBzdHJfYygiWyIsIHJvdW5kKGNvbmYubG93LCAzKSwgIiwgIiwgcm91bmQoY29uZi5oaWdoLCAzKSwgIl0iKSwKICAgIFNpZyA9IHN0YXJzCiAgKQoKY29lZi50YmwgJT4lCiAga2FibGUoCiAgICBkaWdpdHMgPSAzLAogICAgY2FwdGlvbiA9ICJPTFMgUmVncmVzc2lvbiBDb2VmZmljaWVudHMgKEVTRy5JTkRFWCB+IFJFVFVSTi5PTi5BU1NFVFMgKyBGSVJNLlNJWkUgKyBERUJULlRPLkFTU0VUKSIKICApICU+JQogIGthYmxlX3N0eWxpbmcoZnVsbF93aWR0aCA9IEZBTFNFLCBib290c3RyYXBfb3B0aW9ucyA9IGMoInN0cmlwZWQiLCAiaG92ZXIiLCAiY29uZGVuc2VkIikpICU+JQogIGNvbHVtbl9zcGVjKDEsIGJvbGQgPSBUUlVFKSAlPiUKICByb3dfc3BlYygwLCBib2xkID0gVFJVRSwgYmFja2dyb3VuZCA9ICIjZjJmMmYyIikgJT4lCiAgZm9vdG5vdGUoCiAgICBnZW5lcmFsID0gIlNpZ25pZi4gY29kZXM6ICoqKiBwPDAuMDAxLCAqKiBwPDAuMDEsICogcDwwLjA1LCDCtyBwPDAuMS4iLAogICAgdGhyZWVwYXJ0dGFibGUgPSBUUlVFCiAgKQpgYGAKCmBgYHtyfQpmaXQudGJsIDwtIGdsYW5jZShtb2RlbCkgJT4lCiAgdHJhbnNtdXRlKAogICAgYFItc3F1YXJlZGAgPSByLnNxdWFyZWQsCiAgICBgQWRqLiBSLXNxdWFyZWRgID0gYWRqLnIuc3F1YXJlZCwKICAgIGBGLXN0YXRpc3RpY2AgPSBzdGF0aXN0aWMsCiAgICBgRiBwLXZhbHVlYCA9IHAudmFsdWUsCiAgICBgQUlDYCA9IEFJQywKICAgIGBCSUNgID0gQklDLAogICAgYE51bS4gb2JzLmAgPSBub2JzCiAgKQoKZml0LnRibCAlPiUKICBrYWJsZShkaWdpdHMgPSAzLCBjYXB0aW9uID0gIk1vZGVsIEZpdCBTdGF0aXN0aWNzIikgJT4lCiAga2FibGVfc3R5bGluZyhmdWxsX3dpZHRoID0gRkFMU0UsIGJvb3RzdHJhcF9vcHRpb25zID0gYygiY29uZGVuc2VkIikpCmBgYAoKIyBJbmZvIHpkcm9qZSBwcmUgxI9hbMWhaWUgxaF0w7pkaXVtCgojIyMjIFIgUHJvamVjdAoKLSAgIFtSIFByb2plY3QgSG9tZXBhZ2VdKGh0dHBzOi8vd3d3LnItcHJvamVjdC5vcmcvKSAtIEJhc2UgUiBkb3dubG9hZHMsCiAgICBuZXdzLCBhbmQgbGVhcm5pbmcgcmVzb3VyY2VzCgojIyMjIFBvc2l0CgotICAgW1IgU3R1ZGlvIERlc2t0b3BdKGh0dHBzOi8vcG9zaXQuY28vZG93bmxvYWQvcnN0dWRpby1kZXNrdG9wLykgLQogICAgRmVhdHVyZSByaWNoIGVudmlyb25tZW50IGZvciB3b3JraW5nIHdpdGggZGF0YSBpbiBSCi0gICBbUiBmb3IgRGF0YSBTY2llbmNlXShodHRwczovL3I0ZHMuaGFkLmNvLm56LykgLSBDb21wcmVoZW5zaXZlIG9ubGluZQogICAgYm9vawotICAgW1JTdHVkaW8KICAgIENoZWF0c2hlZXRzXShodHRwczovL3d3dy5yc3R1ZGlvLmNvbS9yZXNvdXJjZXMvY2hlYXRzaGVldHMvKSAtIFF1aWNrCiAgICByZWZlcmVuY2UgZ3VpZGVzCgojIyMjIENvbW11bml0eSBSZXNvdXJjZXMKCi0gICBbUi1ibG9nZ2Vyc10oaHR0cHM6Ly93d3cuci1ibG9nZ2Vycy5jb20vKSAtIEJsb2cgYWdncmVnYXRvciBmb3IgUgogICAgbmV3cyBhbmQgdHV0b3JpYWxzCi0gICBbU3RhY2sgT3ZlcmZsb3cgLSBSCiAgICB0YWddKGh0dHBzOi8vc3RhY2tvdmVyZmxvdy5jb20vcXVlc3Rpb25zL3RhZ2dlZC9yKSAtIFEmQSBjb21tdW5pdHkKLSAgIFtyZHJyLmlvIFNuaXBwZXRzXShodHRwczovL3JkcnIuaW8vc25pcHBldHMvKSAtIFRlc3QgUiBjb2RlIHNuaXBwZXRzCiAgICBvbmxpbmUKLSAgIFtDb3Vyc2VyYSAtIFIKICAgIFByb2dyYW1taW5nXShodHRwczovL3d3dy5jb3Vyc2VyYS5vcmcvbGVhcm4vci1wcm9ncmFtbWluZykgLSBPbmxpbmUKICAgIGNvdXJzZQotICAgW0RhdGFDYW1wIC0gSW50cm9kdWN0aW9uIHRvCiAgICBSXShodHRwczovL3d3dy5kYXRhY2FtcC5jb20vY291cnNlcy9mcmVlLWludHJvZHVjdGlvbi10by1yKSAtIEludGVyYWN0aXZlIGxlYXJuaW5nIHBsYXRmb3Jt